pax_global_header00006660000000000000000000000064151215742640014521gustar00rootroot0000000000000052 comment=253ce5089faab6fbe66c220e853b1f39f974153a hydrasdr-SoapyHydraSDR-253ce50/000077500000000000000000000000001512157426400163235ustar00rootroot00000000000000hydrasdr-SoapyHydraSDR-253ce50/.github/000077500000000000000000000000001512157426400176635ustar00rootroot00000000000000hydrasdr-SoapyHydraSDR-253ce50/.github/workflows/000077500000000000000000000000001512157426400217205ustar00rootroot00000000000000hydrasdr-SoapyHydraSDR-253ce50/.github/workflows/build.yml000066400000000000000000001705571512157426400235610ustar00rootroot00000000000000# Copyright (c) 2025, Benjamin Vernoux # # This workflow is licensed exclusively for HydraSDR products and related development. # Unauthorized use, modification, or distribution for non-HydraSDR products is prohibited. # All rights reserved. # # SoapyHydraSDR Build and Release Workflow # For more information about HydraSDR products: https://hydrasdr.com name: Build and Release SoapyHydraSDR Modules (Windows, GNU/Linux, MacOS) on: push: branches: [ main ] pull_request: branches: [ main ] env: # Global environment variables PACKAGE_NAME: "soapyhydrasdr" MAINTAINER: "Benjamin Vernoux " HOMEPAGE: "https://hydrasdr.com" jobs: # Extract version once and share across all jobs extract-version: runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} rfone_version: ${{ steps.rfone_version.outputs.version }} steps: - uses: actions/checkout@v4 - name: Extract SoapyHydraSDR version id: version run: | VERSION=$(grep 'set(SOAPYHYDRASDR_VERSION' CMakeLists.txt | sed 's/.*"\([^"]*\)".*/\1/') echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Extracted SoapyHydraSDR version: $VERSION" - name: Get latest rfone_host release id: rfone_version run: | RFONE_VERSION=$(curl -s https://api.github.com/repos/hydrasdr/rfone_host/releases/latest | jq -r .tag_name) echo "version=$RFONE_VERSION" >> $GITHUB_OUTPUT echo "Latest rfone_host version: $RFONE_VERSION" build: needs: extract-version runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: windows-2022 name: "Windows-x64" platform: "windows" - os: ubuntu-24.04 name: "Ubuntu-24.04-LTS-Noble-Numbat" platform: "linux" create_deb: true - os: macos-14 name: "macOS-ARM64" platform: "macos" arch: "arm64" - os: macos-15-intel name: "macOS-x86_64" platform: "macos" arch: "x86_64" name: ${{ matrix.name }} steps: - uses: actions/checkout@v4 # === Download and Build LibHydraSDR from Source === - name: Download rfone_host source uses: actions/checkout@v4 with: repository: hydrasdr/rfone_host ref: ${{ needs.extract-version.outputs.rfone_version }} path: rfone_host_src - name: Verify rfone_host source download run: | echo "Verifying rfone_host source download..." if [ -d rfone_host_src ]; then echo "Source code downloaded successfully to rfone_host_src/" ls -la rfone_host_src/ | head -10 else echo "Failed to download source code" exit 1 fi shell: bash # === Platform Dependencies === - name: Install Linux dependencies if: matrix.platform == 'linux' run: | sudo apt-get update sudo apt-get install -y build-essential cmake pkg-config libusb-1.0-0-dev # Build SoapySDR from source to get ABI 0.8-3 (apt package has ABI 0.8) # This ensures compatibility with users who build SoapySDR from source # See: https://github.com/hydrasdr/SoapyHydraSDR/issues/7 echo "Building SoapySDR from source for ABI 0.8-3 compatibility..." git clone https://github.com/pothosware/SoapySDR.git /tmp/SoapySDR cd /tmp/SoapySDR mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local make -j$(nproc) sudo make install sudo ldconfig # Verify SoapySDR ABI version echo "Installed SoapySDR version:" SoapySDRUtil --info | head -10 - name: Install macOS dependencies if: matrix.platform == 'macos' env: HOMEBREW_NO_AUTO_UPDATE: 1 run: | brew install --quiet cmake libusb pkg-config # Install SoapySDR brew install --quiet soapysdr # === Windows Dependencies === - name: Setup Windows build tools if: matrix.platform == 'windows' uses: microsoft/setup-msbuild@v2 - name: Install Windows dependencies if: matrix.platform == 'windows' shell: powershell run: | # Install vcpkg and use it to install dependencies git clone https://github.com/Microsoft/vcpkg.git C:\vcpkg C:\vcpkg\bootstrap-vcpkg.bat # Install SoapySDR and libusb C:\vcpkg\vcpkg.exe install soapysdr:x64-windows libusb:x64-windows # Set environment for CMake to find vcpkg packages echo "CMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake" >> $env:GITHUB_ENV - name: Download Windows dependencies for LibHydraSDR if: matrix.platform == 'windows' shell: powershell run: | # Download dependencies for building libhydrasdr Invoke-WebRequest "https://github.com/libusb/libusb/releases/download/v1.0.23/libusb-1.0.23.7z" -OutFile "libusb.7z" Invoke-WebRequest "https://gcc.gnu.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip" -OutFile "pthreads.zip" # Extract libusb New-Item -ItemType Directory -Path "deps\libusb" -Force 7z x libusb.7z -o"deps\libusb" # Extract pthreads Expand-Archive pthreads.zip -DestinationPath "temp" -Force $prebuilt = Get-ChildItem "temp" -Recurse -Directory | Where-Object { $_.Name -eq "Pre-built.2" } New-Item -ItemType Directory -Path "deps\pthreads" -Force Copy-Item "$($prebuilt.FullName)\*" -Destination "deps\pthreads" -Recurse -Force Remove-Item "temp" -Recurse -Force # === Build LibHydraSDR === - name: Build LibHydraSDR (Linux/macOS) if: matrix.platform != 'windows' run: | cd rfone_host_src mkdir build && cd build if [ "${{ matrix.platform }}" = "macos" ]; then if [ "${{ matrix.arch }}" = "arm64" ]; then cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../../rfone_host_build/install -DCMAKE_OSX_ARCHITECTURES=arm64 -DINSTALL_UDEV_RULES=OFF -DENABLE_SHARED_LIB=ON else cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../../rfone_host_build/install -DCMAKE_OSX_ARCHITECTURES=x86_64 -DINSTALL_UDEV_RULES=OFF -DENABLE_SHARED_LIB=ON fi else cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../../rfone_host_build/install -DINSTALL_UDEV_RULES=OFF -DENABLE_SHARED_LIB=ON fi make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) make install # Verify installation echo "=== Verifying LibHydraSDR installation ===" # Check lib directory (lib or lib64 depending on distro) if [ -d "../../rfone_host_build/install/lib" ]; then LIBDIR="../../rfone_host_build/install/lib" elif [ -d "../../rfone_host_build/install/lib64" ]; then LIBDIR="../../rfone_host_build/install/lib64" else echo "No lib or lib64 directory found" exit 1 fi ls -la "$LIBDIR" ls -la ../../rfone_host_build/install/include/ || echo "No include directory found" # Check for specific files based on platform if [ "${{ matrix.platform }}" = "macos" ]; then EXPECTED_LIB="$LIBDIR/libhydrasdr.dylib" else EXPECTED_LIB="$LIBDIR/libhydrasdr.so" fi if [ -f "$EXPECTED_LIB" ]; then echo "✓ Found $(basename $EXPECTED_LIB)" # Only use file command if available if command -v file >/dev/null 2>&1; then file "$EXPECTED_LIB" else ls -l "$EXPECTED_LIB" fi else echo "✗ $(basename $EXPECTED_LIB) not found" echo "Available files in lib directory:" find ../../rfone_host_build/install -name "*hydrasdr*" 2>/dev/null || echo "No hydrasdr files found" exit 1 fi # Check for header file - it might be in libhydrasdr subdirectory HEADER_FOUND=false for header_path in "../../rfone_host_build/install/include/hydrasdr.h" "../../rfone_host_build/install/include/libhydrasdr/hydrasdr.h"; do if [ -f "$header_path" ]; then echo "✓ Found hydrasdr.h at: $header_path" HEADER_FOUND=true break fi done if [ "$HEADER_FOUND" = false ]; then echo "✗ hydrasdr.h not found in expected locations" echo "Available header files:" find ../../rfone_host_build/install/include -name "*.h" 2>/dev/null || echo "No header files found" exit 1 fi - name: Build LibHydraSDR (Windows) if: matrix.platform == 'windows' shell: powershell run: | Set-Location rfone_host_src New-Item -ItemType Directory -Path "build" -Force | Out-Null Set-Location build $currentDir = Get-Location $depsLibusb = Join-Path $currentDir "..\..\deps\libusb" $depsPthreads = Join-Path $currentDir "..\..\deps\pthreads" $installPrefix = Join-Path $currentDir "..\..\rfone_host_build\install" cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_INSTALL_PREFIX="$installPrefix" -DLIBUSB_INCLUDE_DIR="$depsLibusb\include\libusb-1.0" -DLIBUSB_LIBRARIES="$depsLibusb\MS64\dll\libusb-1.0.lib" -DTHREADS_PTHREADS_INCLUDE_DIR="$depsPthreads\include" -DTHREADS_PTHREADS_WIN32_LIBRARY="$depsPthreads\lib\x64\pthreadVC2.lib" -DENABLE_SHARED_LIB=ON cmake --build . --config Release --parallel cmake --install . --config Release Write-Host "Copying runtime dependencies..." $installBinDir = Join-Path $installPrefix "bin" New-Item -ItemType Directory -Path $installBinDir -Force | Out-Null $libusbDll = Join-Path $depsLibusb "MS64\dll\libusb-1.0.dll" $pthreadsDll = Join-Path $depsPthreads "dll\x64\pthreadVC2.dll" if (Test-Path $libusbDll) { Copy-Item $libusbDll $installBinDir -ErrorAction SilentlyContinue Write-Host "Copied libusb-1.0.dll" } if (Test-Path $pthreadsDll) { Copy-Item $pthreadsDll $installBinDir -ErrorAction SilentlyContinue Write-Host "Copied pthreadVC2.dll" } Write-Host "Verifying LibHydraSDR installation..." $libDir = Join-Path $installPrefix "lib" $includeDir = Join-Path $installPrefix "include" if (Test-Path $libDir) { Write-Host "Found lib directory:" Get-ChildItem $libDir } else { Write-Host "No lib directory found" } if (Test-Path $includeDir) { Write-Host "Found include directory:" Get-ChildItem $includeDir } else { Write-Host "No include directory found" } Write-Host "Checking for required files..." $expectedLib = Join-Path $installPrefix "bin\hydrasdr.lib" if (Test-Path $expectedLib) { Write-Host "Found hydrasdr.lib in bin directory - SUCCESS" } else { Write-Host "ERROR: hydrasdr.lib not found in bin directory" Write-Host "Available files in install directory:" Get-ChildItem $installPrefix -Recurse -Filter "*hydrasdr*" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_.FullName } exit 1 } Write-Host "Checking for header files..." $headerFound = $false $headerPath1 = Join-Path $includeDir "hydrasdr.h" $headerPath2 = Join-Path $includeDir "libhydrasdr\hydrasdr.h" if (Test-Path $headerPath1) { Write-Host "Found hydrasdr.h at: $headerPath1" $headerFound = $true } if (Test-Path $headerPath2) { Write-Host "Found hydrasdr.h at: $headerPath2" $headerFound = $true } if ($headerFound -eq $false) { Write-Host "ERROR: hydrasdr.h not found in expected locations" Write-Host "Available header files:" Get-ChildItem $includeDir -Recurse -Filter "*.h" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_.FullName } exit 1 } Write-Host "LibHydraSDR build completed successfully" # === Build SoapyHydraSDR === - name: Configure SoapyHydraSDR build (Linux/macOS) if: matrix.platform != 'windows' run: | mkdir build && cd build if [ "${{ matrix.platform }}" = "macos" ]; then if [ "${{ matrix.arch }}" = "arm64" ]; then cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 else cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 fi else cmake .. -DCMAKE_BUILD_TYPE=Release fi - name: Configure SoapyHydraSDR build (Windows) if: matrix.platform == 'windows' shell: powershell run: | New-Item -ItemType Directory -Path "build" -Force | Out-Null Set-Location build $installPrefix = Resolve-Path "..\rfone_host_build\install" $libhydrasdrLib = Join-Path $installPrefix "bin\hydrasdr.lib" $libhydrasdrInclude = Join-Path $installPrefix "include" Write-Host "Configuring with LibHydraSDR paths:" Write-Host " Install prefix: $installPrefix" Write-Host " Library: $libhydrasdrLib" Write-Host " Include: $libhydrasdrInclude" Write-Host " Library exists: $(Test-Path $libhydrasdrLib)" Write-Host " Include exists: $(Test-Path $libhydrasdrInclude)" # Create a symlink or copy the library to where CMake expects it $expectedLibDir = Join-Path $installPrefix "lib" $expectedLib = Join-Path $expectedLibDir "hydrasdr.lib" if (-not (Test-Path $expectedLibDir)) { New-Item -ItemType Directory -Path $expectedLibDir -Force | Out-Null } if (-not (Test-Path $expectedLib)) { Copy-Item $libhydrasdrLib $expectedLib Write-Host "Copied library to expected location: $expectedLib" } cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE="C:\vcpkg\scripts\buildsystems\vcpkg.cmake" -DCMAKE_PREFIX_PATH="$installPrefix" -DLIBHYDRASDR_ROOT="$installPrefix" # === Build === - name: Build SoapyHydraSDR (Linux/macOS) if: matrix.platform != 'windows' shell: bash run: | cd build if [ "${{ matrix.platform }}" = "macos" ]; then make -j$(sysctl -n hw.ncpu) else make -j$(nproc) fi - name: Build SoapyHydraSDR (Windows) if: matrix.platform == 'windows' shell: powershell run: | cd build cmake --build . --config Release --parallel # === Package Artifacts === - name: Package artifacts (Linux) if: matrix.platform == 'linux' run: | ARTIFACT_DIR="${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}" mkdir -p "$ARTIFACT_DIR" # Find and copy SoapySDR module find build -name "*SoapyHydraSDR*" -type f -exec cp {} "$ARTIFACT_DIR/" \; # Copy built libhydrasdr find rfone_host_build/install -name "libhydrasdr.so*" -exec cp {} "$ARTIFACT_DIR/" \; 2>/dev/null || true - name: Package artifacts (macOS) if: matrix.platform == 'macos' run: | ARTIFACT_DIR="${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}" mkdir -p "$ARTIFACT_DIR" # Find and copy SoapySDR module find build -name "*SoapyHydraSDR*" -type f -exec cp {} "$ARTIFACT_DIR/" \; # Copy built libhydrasdr find rfone_host_build/install -name "libhydrasdr.dylib*" -exec cp {} "$ARTIFACT_DIR/" \; 2>/dev/null || true - name: Package artifacts (Windows) if: matrix.platform == 'windows' shell: powershell run: | $dir = "${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}" New-Item -ItemType Directory -Path $dir -Force # Find and copy only the SoapySDR module DLL (exclude build artifacts) Write-Host "Looking for SoapySDR module files..." Get-ChildItem build -Recurse -Include "*.dll" | Where-Object { $_.Name -like "*SoapyHydraSDR*" } | ForEach-Object { Write-Host "Found SoapySDR module: $($_.FullName)" Copy-Item $_.FullName -Destination $dir -ErrorAction SilentlyContinue } # Copy built libhydrasdr and dependencies Write-Host "Copying LibHydraSDR dependencies..." if (Test-Path "rfone_host_build\install\bin") { Get-ChildItem "rfone_host_build\install\bin" -Filter "*.dll" | Copy-Item -Destination $dir -ErrorAction SilentlyContinue } if (Test-Path "rfone_host_build\install\lib") { Get-ChildItem "rfone_host_build\install\lib" -Filter "*.lib" | Copy-Item -Destination $dir -ErrorAction SilentlyContinue } Write-Host "Final package contents:" Get-ChildItem $dir | ForEach-Object { Write-Host " $($_.Name)" } # === Create .deb package === # FIXED: Install to multiple SoapySDR paths for compatibility with different installations - name: Create .deb package if: matrix.create_deb == true shell: bash run: | PACKAGE_VERSION="${{ needs.extract-version.outputs.version }}" ARCH="amd64" PACKAGE_NAME_FULL="${{ env.PACKAGE_NAME }}" PACKAGE_DIR="${PACKAGE_NAME_FULL}_${PACKAGE_VERSION}_${ARCH}" # Define all common SoapySDR module paths to support different installations # System package path (Debian/Ubuntu apt install) SOAPY_SYSTEM_DIR="/usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8" # Source-built path (cmake default install prefix) SOAPY_LOCAL_DIR="/usr/local/lib/SoapySDR/modules0.8" # Source-built path with ABI version suffix (SoapySDR 0.8.x) SOAPY_LOCAL_ABI_DIR="/usr/local/lib/SoapySDR/modules0.8-3" # Create package directory structure for all paths mkdir -p "${PACKAGE_DIR}/DEBIAN" mkdir -p "${PACKAGE_DIR}${SOAPY_SYSTEM_DIR}" mkdir -p "${PACKAGE_DIR}${SOAPY_LOCAL_DIR}" mkdir -p "${PACKAGE_DIR}${SOAPY_LOCAL_ABI_DIR}" mkdir -p "${PACKAGE_DIR}/usr/lib" mkdir -p "${PACKAGE_DIR}/usr/share/doc/${PACKAGE_NAME_FULL}" # Copy SoapySDR module to ALL common paths for maximum compatibility find build -name "*SoapyHydraSDR*" -type f -exec cp {} "${PACKAGE_DIR}${SOAPY_SYSTEM_DIR}/" \; find build -name "*SoapyHydraSDR*" -type f -exec cp {} "${PACKAGE_DIR}${SOAPY_LOCAL_DIR}/" \; find build -name "*SoapyHydraSDR*" -type f -exec cp {} "${PACKAGE_DIR}${SOAPY_LOCAL_ABI_DIR}/" \; # NOTE: libhydrasdr.so is NOT bundled - provided by hydrasdr-host-tools package # Create control file cat > "${PACKAGE_DIR}/DEBIAN/control" << EOF Package: ${PACKAGE_NAME_FULL} Version: ${PACKAGE_VERSION} Section: electronics Priority: optional Architecture: ${ARCH} Depends: libusb-1.0-0 (>= 1.0.20), hydrasdr-host-tools (>= 1.0.0) Recommends: soapysdr-tools Maintainer: ${{ env.MAINTAINER }} Homepage: ${{ env.HOMEPAGE }} Description: SoapySDR module for HydraSDR RFOne devices SoapySDR driver module for HydraSDR RFOne software-defined radio hardware. Provides seamless integration with SoapySDR ecosystem and applications. EOF # Create postinst script with auto-detection and symlink creation cat > "${PACKAGE_DIR}/DEBIAN/postinst" << 'POSTINST_EOF' #!/bin/bash set -e # SoapyHydraSDR postinst - handles module path detection and symlink creation create_symlink_if_needed() { local target_dir="$1" local module_name="libSoapyHydraSDR.so" local dest_file="${target_dir}/${module_name}" # Skip if target already exists or target directory doesn't exist [ -e "$dest_file" ] && return 0 [ ! -d "$target_dir" ] && return 0 # Find source module in order of preference for src in /usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8/${module_name} \ /usr/local/lib/SoapySDR/modules0.8/${module_name} \ /usr/local/lib/SoapySDR/modules0.8-3/${module_name}; do if [ -f "$src" ]; then ln -sf "$src" "$dest_file" echo "SoapyHydraSDR: Created symlink in $target_dir" return 0 fi done } if [ "$1" = "configure" ]; then # Update library cache first command -v ldconfig >/dev/null 2>&1 && ldconfig || true # Detect actual SoapySDR module search path(s) if command -v SoapySDRUtil >/dev/null 2>&1; then SOAPY_PATHS=$(SoapySDRUtil --info 2>/dev/null | grep "Search path:" | awk '{print $3}') for SOAPY_PATH in $SOAPY_PATHS; do if [ -n "$SOAPY_PATH" ] && [ -d "$SOAPY_PATH" ]; then create_symlink_if_needed "$SOAPY_PATH" fi done # Verify installation echo "SoapyHydraSDR: Verifying installation..." if SoapySDRUtil --info 2>/dev/null | grep -qi "hydrasdr"; then echo "SoapyHydraSDR: Module successfully detected!" else echo "SoapyHydraSDR: Module installed. If not detected, check SoapySDRUtil --info" fi fi fi exit 0 POSTINST_EOF # Create postrm script with proper cleanup cat > "${PACKAGE_DIR}/DEBIAN/postrm" << 'POSTRM_EOF' #!/bin/bash set -e if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then # Remove any symlinks we may have created for path in /usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8 \ /usr/local/lib/SoapySDR/modules0.8 \ /usr/local/lib/SoapySDR/modules0.8-3; do target="${path}/libSoapyHydraSDR.so" if [ -L "$target" ]; then rm -f "$target" fi done command -v ldconfig >/dev/null 2>&1 && ldconfig || true fi exit 0 POSTRM_EOF chmod 755 "${PACKAGE_DIR}/DEBIAN/postinst" "${PACKAGE_DIR}/DEBIAN/postrm" # Create documentation cat > "${PACKAGE_DIR}/usr/share/doc/${PACKAGE_NAME_FULL}/copyright" << EOF Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: SoapyHydraSDR Upstream-Contact: ${{ env.MAINTAINER }} Source: https://github.com/hydrasdr/SoapyHydraSDR Files: * Copyright: 2025 ${{ env.MAINTAINER }} License: Custom-HydraSDR This software is licensed exclusively for HydraSDR products and related development. Unauthorized use, modification, or distribution for non-HydraSDR products is prohibited. All rights reserved. EOF cat > "${PACKAGE_DIR}/usr/share/doc/${PACKAGE_NAME_FULL}/changelog.Debian" << EOF ${PACKAGE_NAME_FULL} (${PACKAGE_VERSION}) unstable; urgency=medium * Automated build from CI for ${{ matrix.name }} * Built with LibHydraSDR ${{ needs.extract-version.outputs.rfone_version }} * Built from commit ${{ github.sha }} * Fixed: Module now installs to multiple SoapySDR paths for compatibility * Fixed: No longer bundles libhydrasdr.so (depends on hydrasdr-host-tools) -- ${{ env.MAINTAINER }} $(date -R) EOF gzip -9 "${PACKAGE_DIR}/usr/share/doc/${PACKAGE_NAME_FULL}/changelog.Debian" # Set permissions find "${PACKAGE_DIR}" -type d -exec chmod 755 {} \; find "${PACKAGE_DIR}${SOAPY_SYSTEM_DIR}" -type f -exec chmod 644 {} \; 2>/dev/null || true find "${PACKAGE_DIR}${SOAPY_LOCAL_DIR}" -type f -exec chmod 644 {} \; 2>/dev/null || true find "${PACKAGE_DIR}${SOAPY_LOCAL_ABI_DIR}" -type f -exec chmod 644 {} \; 2>/dev/null || true find "${PACKAGE_DIR}/usr/lib" -name "*.so*" -exec chmod 644 {} \; # Build package dpkg-deb --build "${PACKAGE_DIR}" # Rename to distribution-specific name DIST_SPECIFIC_NAME="${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${PACKAGE_VERSION}.deb" mv "${PACKAGE_DIR}.deb" "${DIST_SPECIFIC_NAME}" # Verify the package dpkg-deb --info "${DIST_SPECIFIC_NAME}" dpkg-deb --contents "${DIST_SPECIFIC_NAME}" echo "Created distribution-specific .deb package: ${DIST_SPECIFIC_NAME}" # === Testing === - name: Test (Linux) if: matrix.platform == 'linux' run: | echo "Testing SoapySDR module installation..." # Test that libhydrasdr was built correctly export LD_LIBRARY_PATH=rfone_host_build/install/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=rfone_host_build/install/lib64:$LD_LIBRARY_PATH rfone_host_build/install/bin/hydrasdr_lib_version || echo "hydrasdr_lib_version not available" # Test that the SoapySDR module can be found if command -v SoapySDRUtil >/dev/null 2>&1; then # Copy module to temporary location for testing TEMP_MODULE_DIR=$(mktemp -d) find build -name "*SoapyHydraSDR*" -type f -exec cp {} "$TEMP_MODULE_DIR/" \; export SOAPY_SDR_PLUGIN_PATH="$TEMP_MODULE_DIR" echo "Available SoapySDR modules:" SoapySDRUtil --info || echo "SoapySDR info command failed" echo "Looking for HydraSDR devices:" SoapySDRUtil --find || echo "No HydraSDR hardware detected (normal in CI environment)" rm -rf "$TEMP_MODULE_DIR" else echo "SoapySDRUtil not available for testing" fi - name: Test (macOS) if: matrix.platform == 'macos' run: | echo "Testing SoapySDR module installation..." # Test that libhydrasdr was built correctly export DYLD_LIBRARY_PATH=rfone_host_build/install/lib:$DYLD_LIBRARY_PATH rfone_host_build/install/bin/hydrasdr_lib_version || echo "hydrasdr_lib_version not available" if command -v SoapySDRUtil >/dev/null 2>&1; then # Copy module to temporary location for testing TEMP_MODULE_DIR=$(mktemp -d) find build -name "*SoapyHydraSDR*" -type f -exec cp {} "$TEMP_MODULE_DIR/" \; export SOAPY_SDR_PLUGIN_PATH="$TEMP_MODULE_DIR" echo "Available SoapySDR modules:" SoapySDRUtil --info || echo "SoapySDR info command failed" echo "Looking for HydraSDR devices:" SoapySDRUtil --find || echo "No HydraSDR hardware detected (normal in CI environment)" rm -rf "$TEMP_MODULE_DIR" else echo "SoapySDRUtil not available for testing" fi - name: Test (Windows) if: matrix.platform == 'windows' shell: powershell run: | Write-Host "Testing SoapySDR module installation..." Write-Host "Testing libhydrasdr..." # Test libhydrasdr if (Test-Path "rfone_host_build\install\bin\hydrasdr_lib_version.exe") { try { & "rfone_host_build\install\bin\hydrasdr_lib_version.exe" Write-Host "LibHydraSDR test completed successfully" } catch { Write-Host "hydrasdr_lib_version test failed (normal if hardware not connected)" } } else { Write-Host "hydrasdr_lib_version.exe not found" } # Test SoapySDR $SoapySDRUtil = "C:\vcpkg\installed\x64-windows\bin\SoapySDRUtil.exe" if (Test-Path $SoapySDRUtil) { Write-Host "Found SoapySDRUtil, running tests..." try { Write-Host "Available SoapySDR modules:" & $SoapySDRUtil --info Write-Host "Looking for HydraSDR devices:" & $SoapySDRUtil --find Write-Host "SoapySDR tests completed successfully" } catch { Write-Host "SoapySDR tests failed (normal in CI environment without hardware)" } } else { Write-Host "SoapySDRUtil not available - skipping SoapySDR tests (this is normal)" } Write-Host "Windows testing completed - all tests are optional in CI environment" exit 0 # === Upload === - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }} path: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}/* retention-days: 30 - name: Upload .deb package if: matrix.create_deb == true uses: actions/upload-artifact@v4 with: name: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}-deb path: "${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v*.deb" retention-days: 30 build_linux_distros: needs: extract-version runs-on: ubuntu-latest container: ${{ matrix.container }} strategy: fail-fast: false matrix: include: # Ubuntu LTS Versions # Note: We build SoapySDR from source to get ABI 0.8-3 compatibility # See: https://github.com/hydrasdr/SoapyHydraSDR/issues/7 - container: "ubuntu:22.04" name: "Ubuntu-22.04-LTS-Jammy-Jellyfish" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" - container: "ubuntu:20.04" name: "Ubuntu-20.04-LTS-Focal-Fossa" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" # Debian Stable/Testing - container: "debian:trixie" name: "Debian-13-Trixie" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" - container: "debian:bookworm" name: "Debian-12-Bookworm" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" - container: "debian:bullseye" name: "Debian-11-Bullseye" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" # Linux Mint Versions (Ubuntu-based) - container: "linuxmintd/mint22-amd64:latest" name: "Linux-Mint-22-Wilma" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" - container: "linuxmintd/mint21.3-amd64:latest" name: "Linux-Mint-21.3-Virginia" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" - container: "linuxmintd/mint21.2-amd64:latest" name: "Linux-Mint-21.2-Victoria" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" - container: "linuxmintd/mint21.1-amd64:latest" name: "Linux-Mint-21.1-Vera" pkg_manager: "apt" create_deb: true build_soapysdr: true deps: "build-essential cmake pkg-config libusb-1.0-0-dev git wget curl ca-certificates" # Fedora Latest Versions - container: "fedora:42" name: "Fedora-42" pkg_manager: "dnf" create_rpm: true deps: "gcc gcc-c++ cmake pkg-config libusb1-devel make rpm-build rpmdevtools tar gzip SoapySDR-devel wget ca-certificates chrpath" - container: "fedora:41" name: "Fedora-41" pkg_manager: "dnf" create_rpm: true deps: "gcc gcc-c++ cmake pkg-config libusb1-devel make rpm-build rpmdevtools tar gzip SoapySDR-devel wget ca-certificates chrpath" # openSUSE - container: "opensuse/tumbleweed:latest" name: "openSUSE-Tumbleweed" pkg_manager: "zypper" create_rpm: true deps: "gcc gcc-c++ cmake pkgconfig libusb-1_0-devel make rpm-build tar gzip soapy-sdr-devel wget ca-certificates chrpath" # Enterprise Linux - container: "almalinux:9" name: "AlmaLinux-9" pkg_manager: "dnf" create_rpm: true deps: "gcc gcc-c++ cmake pkg-config libusb1-devel make epel-release rpm-build rpmdevtools wget ca-certificates chrpath" enable_repos: true allow_erasing: true # Rolling Release Distributions - container: "archlinux:latest" name: "Arch-Linux" pkg_manager: "pacman" create_pkg: true deps: "base-devel cmake pkg-config libusb sudo soapysdr wget curl ca-certificates" name: ${{ matrix.name }} steps: - uses: actions/checkout@v4 # === Download and Build LibHydraSDR from Source === - name: Download rfone_host source uses: actions/checkout@v4 with: repository: hydrasdr/rfone_host ref: ${{ needs.extract-version.outputs.rfone_version }} path: rfone_host_src - name: Verify rfone_host source download shell: bash run: | echo "Verifying rfone_host source download..." if [ -d rfone_host_src ]; then echo "Source code downloaded successfully to rfone_host_src/" ls -la rfone_host_src/ | head -10 else echo "Failed to download source code" exit 1 fi # === Install dependencies === - name: Install dependencies run: | case "${{ matrix.pkg_manager }}" in "apt") export DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get update apt-get install -y ${{ matrix.deps }} ;; "dnf") dnf update -y # Enable additional repositories for RHEL-based systems if [[ "${{ matrix.name }}" == *"AlmaLinux"* ]]; then dnf install -y epel-release # Enable CodeReady Builder (CRB) repository for AlmaLinux 9 dnf config-manager --set-enabled crb || dnf config-manager --set-enabled powertools || true # Install SoapySDR from EPEL dnf install -y SoapySDR-devel fi # Handle curl conflicts on RHEL-based systems if [[ "${{ matrix.allow_erasing }}" == "true" ]]; then echo "Installing with --allowerasing to handle curl conflicts..." dnf install -y --allowerasing ${{ matrix.deps }} else dnf install -y ${{ matrix.deps }} fi ;; "zypper") zypper refresh # Handle special package names for openSUSE if [[ "${{ matrix.name }}" == *"openSUSE"* ]]; then # Try alternative package names if the main ones fail zypper install -y gcc gcc-c++ cmake pkgconfig libusb-1_0-devel make rpm-build tar gzip wget ca-certificates chrpath || true # Try to install SoapySDR - it might have different names zypper install -y soapy-sdr-devel || zypper install -y SoapySDR-devel || zypper install -y libsoapysdr-devel || echo "SoapySDR not available, will build without" else zypper install -y ${{ matrix.deps }} fi ;; "pacman") pacman -Sy --noconfirm archlinux-keyring pacman -Syu --noconfirm pacman -S --noconfirm ${{ matrix.deps }} ;; esac # === Build SoapySDR from source for ABI 0.8-3 compatibility === # This is required for Debian/Ubuntu because apt packages have older ABI 0.8 # Users who build SoapySDR from source get ABI 0.8-3 # See: https://github.com/hydrasdr/SoapyHydraSDR/issues/7 - name: Build SoapySDR from source if: matrix.build_soapysdr == true run: | echo "Building SoapySDR from source for ABI 0.8-3 compatibility..." git clone https://github.com/pothosware/SoapySDR.git /tmp/SoapySDR cd /tmp/SoapySDR mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local make -j$(nproc) make install ldconfig # Verify SoapySDR ABI version echo "=== Installed SoapySDR version ===" SoapySDRUtil --info | head -10 # === Build LibHydraSDR === - name: Build LibHydraSDR run: | cd rfone_host_src mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../../rfone_host_build/install -DINSTALL_UDEV_RULES=OFF -DENABLE_SHARED_LIB=ON make -j$(nproc) make install # Verify installation echo "=== Verifying LibHydraSDR installation ===" # Check lib directory (lib or lib64 depending on distro) if [ -d "../../rfone_host_build/install/lib" ]; then LIBDIR="../../rfone_host_build/install/lib" elif [ -d "../../rfone_host_build/install/lib64" ]; then LIBDIR="../../rfone_host_build/install/lib64" else echo "No lib or lib64 directory found" exit 1 fi ls -la "$LIBDIR" ls -la ../../rfone_host_build/install/include/ || echo "No include directory found" # Check for specific files if [ -f "$LIBDIR/libhydrasdr.so" ]; then echo "✓ Found libhydrasdr.so" # Only use file command if available if command -v file >/dev/null 2>&1; then file "$LIBDIR/libhydrasdr.so" else ls -l "$LIBDIR/libhydrasdr.so" fi else echo "✗ libhydrasdr.so not found" echo "Available files in lib directory:" find ../../rfone_host_build/install -name "*hydrasdr*" 2>/dev/null || echo "No hydrasdr files found" exit 1 fi # Check for header file - it might be in libhydrasdr subdirectory HEADER_FOUND=false for header_path in "../../rfone_host_build/install/include/hydrasdr.h" "../../rfone_host_build/install/include/libhydrasdr/hydrasdr.h"; do if [ -f "$header_path" ]; then echo "✓ Found hydrasdr.h at: $header_path" HEADER_FOUND=true break fi done if [ "$HEADER_FOUND" = false ]; then echo "✗ hydrasdr.h not found in expected locations" echo "Available header files:" find ../../rfone_host_build/install/include -name "*.h" 2>/dev/null || echo "No header files found" exit 1 fi # === Build SoapyHydraSDR === - name: Configure and build SoapyHydraSDR run: | mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j$(nproc) # === Package Artifacts === - name: Package artifacts run: | ARTIFACT_DIR="${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}" mkdir -p "$ARTIFACT_DIR" find build -name "*SoapyHydraSDR*" -type f -exec cp {} "$ARTIFACT_DIR/" \; find rfone_host_build/install -name "libhydrasdr.so*" -exec cp {} "$ARTIFACT_DIR/" \; 2>/dev/null || true # === Create distribution packages === # FIXED: Install to multiple SoapySDR paths for compatibility with different installations - name: Create .deb package if: matrix.create_deb == true shell: bash run: | PACKAGE_VERSION="${{ needs.extract-version.outputs.version }}" ARCH="amd64" PACKAGE_NAME_FULL="${{ env.PACKAGE_NAME }}" PACKAGE_DIR="${PACKAGE_NAME_FULL}_${PACKAGE_VERSION}_${ARCH}" # Define all common SoapySDR module paths SOAPY_SYSTEM_DIR="/usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8" SOAPY_LOCAL_DIR="/usr/local/lib/SoapySDR/modules0.8" SOAPY_LOCAL_ABI_DIR="/usr/local/lib/SoapySDR/modules0.8-3" # Create package directory structure for all paths mkdir -p "${PACKAGE_DIR}/DEBIAN" mkdir -p "${PACKAGE_DIR}${SOAPY_SYSTEM_DIR}" mkdir -p "${PACKAGE_DIR}${SOAPY_LOCAL_DIR}" mkdir -p "${PACKAGE_DIR}${SOAPY_LOCAL_ABI_DIR}" mkdir -p "${PACKAGE_DIR}/usr/share/doc/${PACKAGE_NAME_FULL}" # Copy to ALL common paths find build -name "*SoapyHydraSDR*" -type f -exec cp {} "${PACKAGE_DIR}${SOAPY_SYSTEM_DIR}/" \; find build -name "*SoapyHydraSDR*" -type f -exec cp {} "${PACKAGE_DIR}${SOAPY_LOCAL_DIR}/" \; find build -name "*SoapyHydraSDR*" -type f -exec cp {} "${PACKAGE_DIR}${SOAPY_LOCAL_ABI_DIR}/" \; # NOTE: libhydrasdr.so is NOT bundled - provided by hydrasdr-host-tools package cat > "${PACKAGE_DIR}/DEBIAN/control" << EOF Package: ${PACKAGE_NAME_FULL} Version: ${PACKAGE_VERSION} Section: electronics Priority: optional Architecture: ${ARCH} Depends: libusb-1.0-0 (>= 1.0.20), hydrasdr-host-tools (>= 1.0.0) Recommends: soapysdr-tools Maintainer: ${{ env.MAINTAINER }} Homepage: ${{ env.HOMEPAGE }} Description: SoapySDR module for HydraSDR RFOne devices SoapySDR driver module for HydraSDR RFOne software-defined radio hardware. Provides seamless integration with SoapySDR ecosystem and applications. EOF # Create postinst with auto-detection cat > "${PACKAGE_DIR}/DEBIAN/postinst" << 'POSTINST_EOF' #!/bin/bash set -e create_symlink_if_needed() { local target_dir="$1" local dest_file="${target_dir}/libSoapyHydraSDR.so" [ -e "$dest_file" ] && return 0 [ ! -d "$target_dir" ] && return 0 for src in /usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8/libSoapyHydraSDR.so \ /usr/local/lib/SoapySDR/modules0.8/libSoapyHydraSDR.so \ /usr/local/lib/SoapySDR/modules0.8-3/libSoapyHydraSDR.so; do if [ -f "$src" ]; then ln -sf "$src" "$dest_file" return 0 fi done } if [ "$1" = "configure" ]; then ldconfig || true if command -v SoapySDRUtil >/dev/null 2>&1; then SOAPY_PATHS=$(SoapySDRUtil --info 2>/dev/null | grep "Search path:" | awk '{print $3}') for SOAPY_PATH in $SOAPY_PATHS; do [ -n "$SOAPY_PATH" ] && [ -d "$SOAPY_PATH" ] && create_symlink_if_needed "$SOAPY_PATH" done SoapySDRUtil --info 2>/dev/null | grep -qi "hydrasdr" && echo "SoapyHydraSDR: Module detected!" || true fi fi exit 0 POSTINST_EOF cat > "${PACKAGE_DIR}/DEBIAN/postrm" << 'POSTRM_EOF' #!/bin/bash set -e if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then for path in /usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8 \ /usr/local/lib/SoapySDR/modules0.8 \ /usr/local/lib/SoapySDR/modules0.8-3; do [ -L "${path}/libSoapyHydraSDR.so" ] && rm -f "${path}/libSoapyHydraSDR.so" done ldconfig || true fi exit 0 POSTRM_EOF chmod 755 "${PACKAGE_DIR}/DEBIAN/postinst" "${PACKAGE_DIR}/DEBIAN/postrm" find "${PACKAGE_DIR}" -type d -exec chmod 755 {} \; find "${PACKAGE_DIR}${SOAPY_SYSTEM_DIR}" -type f -exec chmod 644 {} \; 2>/dev/null || true find "${PACKAGE_DIR}${SOAPY_LOCAL_DIR}" -type f -exec chmod 644 {} \; 2>/dev/null || true find "${PACKAGE_DIR}${SOAPY_LOCAL_ABI_DIR}" -type f -exec chmod 644 {} \; 2>/dev/null || true find "${PACKAGE_DIR}/usr/lib" -name "*.so*" -exec chmod 644 {} \; dpkg-deb --build "${PACKAGE_DIR}" mv "${PACKAGE_DIR}.deb" "${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${PACKAGE_VERSION}.deb" - name: Create .rpm package if: matrix.create_rpm == true shell: bash run: | PACKAGE_VERSION="${{ needs.extract-version.outputs.version }}" PACKAGE_NAME_FULL="${{ env.PACKAGE_NAME }}" BUILD_DIR="$(pwd)" # Setup RPM build environment (different for openSUSE vs Fedora/RHEL) if [[ "${{ matrix.pkg_manager }}" == "zypper" ]]; then # openSUSE uses /usr/src/packages (already set by system) RPM_BUILD_ROOT="/usr/src/packages" mkdir -p ${RPM_BUILD_ROOT}/{BUILD,RPMS,SOURCES,SPECS,SRPMS} USE_CUSTOM_TOPDIR=false else # Fedora/RHEL uses ~/rpmbuild rpmdev-setuptree || mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} RPM_BUILD_ROOT="$HOME/rpmbuild" USE_CUSTOM_TOPDIR=true fi # Get SoapySDR module directories - support both package and source-built SoapySDR # See: https://github.com/hydrasdr/SoapyHydraSDR/issues/7 SOAPY_MODULE_DIR="/usr/lib64/SoapySDR/modules0.8" SOAPY_LOCAL_DIR="/usr/local/lib64/SoapySDR/modules0.8-3" # Create temporary build structure BUILD_ROOT="${RPM_BUILD_ROOT}/BUILD/${PACKAGE_NAME_FULL}-${PACKAGE_VERSION}" mkdir -p "${BUILD_ROOT}" # Copy built files to temporary location for packaging TEMP_INSTALL_DIR="${BUILD_ROOT}/temp_install" mkdir -p "${TEMP_INSTALL_DIR}${SOAPY_MODULE_DIR}" mkdir -p "${TEMP_INSTALL_DIR}${SOAPY_LOCAL_DIR}" # Copy SoapySDR module to both paths and strip problematic RPATHs find "${BUILD_DIR}/build" -name "*SoapyHydraSDR*" -type f -exec cp {} "${TEMP_INSTALL_DIR}${SOAPY_MODULE_DIR}/" \; 2>/dev/null || echo "No SoapyHydraSDR module found" find "${BUILD_DIR}/build" -name "*SoapyHydraSDR*" -type f -exec cp {} "${TEMP_INSTALL_DIR}${SOAPY_LOCAL_DIR}/" \; 2>/dev/null || true # Strip RPATHs from SoapySDR modules to avoid packaging issues if command -v chrpath >/dev/null 2>&1; then find "${TEMP_INSTALL_DIR}" -name "*.so" -exec chrpath -d {} \; 2>/dev/null || true elif command -v patchelf >/dev/null 2>&1; then find "${TEMP_INSTALL_DIR}" -name "*.so" -exec patchelf --remove-rpath {} \; 2>/dev/null || true fi # NOTE: libhydrasdr.so is NOT bundled - provided by hydrasdr-host-tools package # List what we actually have echo "=== Files prepared for RPM packaging ===" find "${TEMP_INSTALL_DIR}" -type f | head -20 # Create spec file with proper changelog format cat > ${RPM_BUILD_ROOT}/SPECS/${PACKAGE_NAME_FULL}.spec << 'EOF' Name: soapyhydrasdr Version: %{version} Release: 1%{?dist} Summary: SoapySDR module for HydraSDR RFOne devices License: Custom-HydraSDR URL: %{homepage} BuildArch: x86_64 BuildRequires: gcc-c++, cmake %if 0%{?suse_version} BuildRequires: pkgconfig, libusb-1_0-devel, soapy-sdr-devel Requires: libusb-1_0-0, soapy-sdr, hydrasdr-host-tools >= 1.0.0 %else BuildRequires: pkg-config, libusb1-devel, SoapySDR-devel Requires: libusb1, SoapySDR, hydrasdr-host-tools >= 1.0.0 %endif %description SoapySDR driver module for HydraSDR RFOne software-defined radio hardware. Provides seamless integration with SoapySDR ecosystem and applications. %prep # No prep needed for binary package %build # No build needed for binary package %install rm -rf %{buildroot} mkdir -p %{buildroot}/usr/lib64/SoapySDR/modules0.8 mkdir -p %{buildroot}/usr/local/lib64/SoapySDR/modules0.8-3 # Copy SoapySDR module files from temporary install location if [ -d "%{temp_install}/usr/lib64/SoapySDR/modules0.8" ]; then cp -a "%{temp_install}/usr/lib64/SoapySDR/modules0.8"/* %{buildroot}/usr/lib64/SoapySDR/modules0.8/ 2>/dev/null || true fi if [ -d "%{temp_install}/usr/local/lib64/SoapySDR/modules0.8-3" ]; then cp -a "%{temp_install}/usr/local/lib64/SoapySDR/modules0.8-3"/* %{buildroot}/usr/local/lib64/SoapySDR/modules0.8-3/ 2>/dev/null || true fi %files %defattr(-,root,root,-) /usr/lib64/SoapySDR/modules0.8/* /usr/local/lib64/SoapySDR/modules0.8-3/* %post /sbin/ldconfig if command -v SoapySDRUtil >/dev/null 2>&1; then SoapySDRUtil --info >/dev/null 2>&1 || true fi %postun /sbin/ldconfig %changelog * Tue Jul 30 2024 Benjamin Vernoux - %{version}-1 - Automated build from CI for %{distname} - Built with LibHydraSDR %{rfone_version} - Fixed: No longer bundles libhydrasdr.so (depends on hydrasdr-host-tools) EOF # Build RPM with RPATH checking disabled and variable substitution cd ${RPM_BUILD_ROOT}/SPECS # Choose the right rpmbuild command based on whether we need custom topdir if [[ "$USE_CUSTOM_TOPDIR" == "true" ]]; then rpmbuild -bb \ --define "_topdir ${RPM_BUILD_ROOT}" \ --define "version ${PACKAGE_VERSION}" \ --define "homepage ${{ env.HOMEPAGE }}" \ --define "distname ${{ matrix.name }}" \ --define "rfone_version ${{ needs.extract-version.outputs.rfone_version }}" \ --define "temp_install ${TEMP_INSTALL_DIR}" \ --define "__brp_check_rpaths %{nil}" \ --define "_binaries_in_noarch_packages_terminate_build 0" \ ${PACKAGE_NAME_FULL}.spec else rpmbuild -bb \ --define "version ${PACKAGE_VERSION}" \ --define "homepage ${{ env.HOMEPAGE }}" \ --define "distname ${{ matrix.name }}" \ --define "rfone_version ${{ needs.extract-version.outputs.rfone_version }}" \ --define "temp_install ${TEMP_INSTALL_DIR}" \ --define "__brp_check_rpaths %{nil}" \ --define "_binaries_in_noarch_packages_terminate_build 0" \ ${PACKAGE_NAME_FULL}.spec fi # Copy RPM to build directory with consistent naming if [[ "${{ matrix.pkg_manager }}" == "zypper" ]]; then # openSUSE puts RPMs in /usr/src/packages/RPMS find ${RPM_BUILD_ROOT}/RPMS -name "*.rpm" -ls find ${RPM_BUILD_ROOT}/RPMS -name "*.rpm" -exec cp {} "${BUILD_DIR}/${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${PACKAGE_VERSION}.rpm" \; else # Fedora/RHEL puts RPMs in ~/rpmbuild/RPMS find ~/rpmbuild/RPMS -name "*.rpm" -ls find ~/rpmbuild/RPMS -name "*.rpm" -exec cp {} "${BUILD_DIR}/${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${PACKAGE_VERSION}.rpm" \; fi # Verify RPM was created if [ -f "${BUILD_DIR}/${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${PACKAGE_VERSION}.rpm" ]; then echo "SUCCESS: Created RPM package: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${PACKAGE_VERSION}.rpm" ls -la "${BUILD_DIR}/${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${PACKAGE_VERSION}.rpm" else echo "ERROR: Failed to create RPM package" echo "Available RPM files in all locations:" find ${RPM_BUILD_ROOT}/RPMS -name "*.rpm" -ls 2>/dev/null || echo "No RPM files in ${RPM_BUILD_ROOT}/RPMS" find ~/rpmbuild/RPMS -name "*.rpm" -ls 2>/dev/null || echo "No RPM files in ~/rpmbuild/RPMS" exit 1 fi # === Upload artifacts === - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }} path: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}/* retention-days: 30 - name: Upload .deb package if: matrix.create_deb == true uses: actions/upload-artifact@v4 with: name: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}-deb path: "${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v*.deb" retention-days: 30 - name: Pre-upload RPM verification if: matrix.create_rpm == true run: | echo "=== Pre-upload RPM verification ===" echo "Job: build_linux_distros" echo "Matrix name: ${{ matrix.name }}" echo "Matrix create_rpm: ${{ matrix.create_rpm }}" echo "Expected RPM: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}.rpm" echo "Upload pattern: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v*.rpm" echo "" # Check if the specific file exists RPM_FILE="${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}.rpm" if [ -f "$RPM_FILE" ]; then echo "✅ Target RPM file exists: $RPM_FILE" ls -la "$RPM_FILE" else echo "❌ Target RPM file missing: $RPM_FILE" exit 1 fi # Check what files match the upload pattern echo "" echo "=== Files matching upload pattern ===" ls -la ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v*.rpm 2>/dev/null || echo "No files match the upload pattern" # Double-check working directory echo "" echo "=== Current working directory ===" pwd echo "" echo "=== All RPM files in current directory ===" find . -maxdepth 1 -name "*.rpm" -type f -exec ls -la {} \; - name: Upload .rpm package if: matrix.create_rpm == true uses: actions/upload-artifact@v4 with: name: ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}-rpm path: "${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v*.rpm" retention-days: 30 if-no-files-found: error - name: Post-upload RPM confirmation if: matrix.create_rpm == true run: | echo "=== Post-upload confirmation ===" echo "Job: build_linux_distros" echo "If you see this message, the upload step completed" echo "Check the GitHub Actions Artifacts section for:" echo " ${{ env.PACKAGE_NAME }}-${{ matrix.name }}-v${{ needs.extract-version.outputs.version }}-rpm" create_full_archive: needs: [extract-version, build, build_linux_distros] runs-on: ubuntu-latest steps: - name: Download All Builds uses: actions/download-artifact@v4 - name: Create platform-specific archives run: | mkdir soapyhydrasdr_all # Copy all native package files echo "=== Looking for .deb packages ===" find . -name "${{ env.PACKAGE_NAME }}-*-v*.deb" -exec ls -la {} \; -exec cp {} soapyhydrasdr_all/ \; echo "=== Looking for .rpm packages ===" find . -name "${{ env.PACKAGE_NAME }}-*-v*.rpm" -exec ls -la {} \; -exec cp {} soapyhydrasdr_all/ \; echo "=== Looking for .pkg.tar.* packages ===" find . -name "${{ env.PACKAGE_NAME }}-*-v*.pkg.tar.*" -exec ls -la {} \; -exec cp {} soapyhydrasdr_all/ \; # Create archives for platforms without native packages for dir in ${{ env.PACKAGE_NAME }}-*; do if [ -d "$dir" ]; then if [[ "$dir" == *"-deb" ]] || [[ "$dir" == *"-rpm" ]] || [[ "$dir" == *"-pkg" ]]; then continue fi if [[ "$dir" == *"Windows"* ]]; then cd "$dir" && zip -r "../soapyhydrasdr_all/${dir}.zip" . && cd .. elif [[ "$dir" == *"macOS"* ]]; then tar -czf "soapyhydrasdr_all/${dir}.tar.gz" -C "$dir" . elif [[ "$dir" == *"Ubuntu-24.04"* ]] || [[ "$dir" == *"Ubuntu-22.04"* ]]; then # Always create ZIP archives for Ubuntu (CI/CD requirement) cd "$dir" && zip -r "../soapyhydrasdr_all/${dir}.zip" . && cd .. fi fi done echo "=== Final contents of soapyhydrasdr_all ===" ls -la soapyhydrasdr_all/ echo "=== Summary of package types ===" echo "DEB packages:" ls soapyhydrasdr_all/*.deb 2>/dev/null || echo "No .deb packages found" echo "RPM packages:" ls soapyhydrasdr_all/*.rpm 2>/dev/null || echo "No .rpm packages found" echo "PKG packages:" ls soapyhydrasdr_all/*.pkg.tar.* 2>/dev/null || echo "No .pkg packages found" echo "Archive files:" ls soapyhydrasdr_all/*.{zip,tar.gz} 2>/dev/null || echo "No archive files found" - name: Upload Full Archive uses: actions/upload-artifact@v4 with: name: soapyhydrasdr_all path: soapyhydrasdr_all/* update_nightly_release: needs: [extract-version, create_full_archive] runs-on: ubuntu-latest if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} steps: - name: Download All Builds uses: actions/download-artifact@v4 - name: Create or Update Nightly Release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Delete existing nightly release gh release delete nightly -R ${{github.repository}} --yes || echo "Nightly release doesn't exist" # Create fresh nightly release gh release create nightly -R ${{github.repository}} --title "Nightly Build" --notes "Automated nightly build from CI - Built from commit ${{ github.sha }} with LibHydraSDR ${{ needs.extract-version.outputs.rfone_version }}" --prerelease # Upload platform-specific archives if [ -d "soapyhydrasdr_all" ] && [ "$(ls -A soapyhydrasdr_all)" ]; then gh release upload nightly soapyhydrasdr_all/* -R ${{github.repository}} fi - name: Update Release Notes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release edit nightly -R ${{github.repository}} --notes "Automated nightly build from CI **Built from commit:** ${{ github.sha }} **Build Date:** $(date -u) **Branch:** ${{ github.ref_name }} **SoapyHydraSDR Version:** ${{ needs.extract-version.outputs.version }} **LibHydraSDR Version:** ${{ needs.extract-version.outputs.rfone_version }} ## SoapySDR Module for HydraSDR RFOne This release provides SoapySDR driver modules for HydraSDR RFOne software-defined radio hardware, enabling seamless integration with the SoapySDR ecosystem and applications like GQRX, SDR++, GNU Radio, and more. **Key Features:** - Built with latest LibHydraSDR (${{ needs.extract-version.outputs.rfone_version }}) from source - **Built against SoapySDR ABI 0.8-3** for compatibility with source-built SoapySDR - No hardcoded dependencies - always uses latest HydraSDR host libraries - Full SoapySDR integration for all compatible applications - Multi-platform native packages with automatic dependency management - Comprehensive distribution support - Module installs to multiple SoapySDR paths for maximum compatibility **Important:** For best compatibility, build SoapySDR from source: \`\`\`bash git clone https://github.com/pothosware/SoapySDR.git cd SoapySDR && mkdir build && cd build cmake .. && make -j\$(nproc) && sudo make install && sudo ldconfig \`\`\` For detailed installation instructions and usage information, see the project README." hydrasdr-SoapyHydraSDR-253ce50/.gitignore000066400000000000000000000000261512157426400203110ustar00rootroot00000000000000build/ .vscode/ obj-* hydrasdr-SoapyHydraSDR-253ce50/CMakeLists.txt000066400000000000000000000361701512157426400210720ustar00rootroot00000000000000################################################### # Build Soapy SDR support module for HydraSDR Devices ################################################### cmake_minimum_required(VERSION 3.10) # Project version - UPDATE THIS FOR RELEASES set(SOAPYHYDRASDR_VERSION "1.0.1") project(SoapyHydraSDR VERSION ${SOAPYHYDRASDR_VERSION} LANGUAGES CXX) message(STATUS "Building SoapyHydraSDR version: ${SOAPYHYDRASDR_VERSION}") # Set default build type to Release if not specified if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() # Platform-specific settings if(WIN32) # Use static runtime on Windows for better compatibility set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") # Set proper Windows subsystem if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(CMAKE_WIN32_EXECUTABLE OFF) endif() endif() # Find required dependencies find_package(SoapySDR "0.4.0" NO_MODULE REQUIRED) if(NOT SoapySDR_FOUND) message(FATAL_ERROR "Soapy SDR development files not found...") endif() message(STATUS "Found SoapySDR: ${SoapySDR_VERSION}") # Ensure SoapySDR module directory is set if(NOT SOAPY_SDR_MODULE_DIR) # Try to detect SoapySDR module directory using pkg-config first (most reliable) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_check_modules(SOAPYSDR_PC QUIET SoapySDR) if(SOAPYSDR_PC_FOUND) execute_process( COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=moduledir SoapySDR OUTPUT_VARIABLE SOAPY_SDR_MODULE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) if(SOAPY_SDR_MODULE_DIR) message(STATUS "Detected SoapySDR module directory via pkg-config: ${SOAPY_SDR_MODULE_DIR}") endif() endif() endif() # Extract SoapySDR ABI version from module directory or version string if(SOAPY_SDR_MODULE_DIR) # Extract ABI version from the detected module path (e.g., modules0.8-3 -> 0.8-3) string(REGEX MATCH "modules([0-9]+\\.[0-9]+(-[0-9]+)?)" _match "${SOAPY_SDR_MODULE_DIR}") if(_match) set(SoapySDR_ABI_VERSION "${CMAKE_MATCH_1}") message(STATUS "Extracted SoapySDR ABI version from module path: ${SoapySDR_ABI_VERSION}") endif() endif() # Fallback: extract ABI version from SoapySDR version string if(NOT SoapySDR_ABI_VERSION AND SoapySDR_VERSION) # For SoapySDR 0.8.x, the ABI version is "0.8-3" for source builds # For SoapySDR 0.8.0 from packages, the ABI version is "0.8" string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _version_match "${SoapySDR_VERSION}") if(_version_match) set(_major "${CMAKE_MATCH_1}") set(_minor "${CMAKE_MATCH_2}") set(_patch "${CMAKE_MATCH_3}") # SoapySDR 0.8.1+ uses ABI 0.8-3 if("${_major}.${_minor}" STREQUAL "0.8" AND _patch GREATER 0) set(SoapySDR_ABI_VERSION "0.8-3") else() set(SoapySDR_ABI_VERSION "${_major}.${_minor}") endif() message(STATUS "Determined SoapySDR ABI version from version ${SoapySDR_VERSION}: ${SoapySDR_ABI_VERSION}") else() # Simple major.minor extraction as last resort string(REGEX MATCH "^([0-9]+\\.[0-9]+)" SoapySDR_ABI_VERSION "${SoapySDR_VERSION}") message(STATUS "Extracted SoapySDR ABI version: ${SoapySDR_ABI_VERSION}") endif() endif() if(NOT SoapySDR_ABI_VERSION) set(SoapySDR_ABI_VERSION "0.8") # Fallback version message(STATUS "Using fallback SoapySDR ABI version: ${SoapySDR_ABI_VERSION}") endif() # Fallback to common locations if module directory not found via pkg-config if(NOT SOAPY_SDR_MODULE_DIR) if(WIN32) # Windows with vcpkg - try to find existing module directory # vcpkg typically uses modules0.8 even for SoapySDR 0.8.1+ foreach(POSSIBLE_ABI "0.8" "0.8-3" "${SoapySDR_ABI_VERSION}") set(_test_dir "${CMAKE_PREFIX_PATH}/lib/SoapySDR/modules${POSSIBLE_ABI}") if(EXISTS "${_test_dir}") set(SOAPY_SDR_MODULE_DIR "${_test_dir}") set(SoapySDR_ABI_VERSION "${POSSIBLE_ABI}") message(STATUS "Found Windows SoapySDR module directory: ${SOAPY_SDR_MODULE_DIR}") break() endif() endforeach() # Fallback to relative path with detected ABI version if(NOT SOAPY_SDR_MODULE_DIR) set(SOAPY_SDR_MODULE_DIR "lib/SoapySDR/modules${SoapySDR_ABI_VERSION}") endif() elseif(APPLE) # Try multiple paths on macOS foreach(POSSIBLE_DIR "/usr/local/lib/SoapySDR/modules${SoapySDR_ABI_VERSION}" "/usr/local/lib/SoapySDR/modules0.8-3" "/usr/local/lib/SoapySDR/modules0.8" "/opt/homebrew/lib/SoapySDR/modules${SoapySDR_ABI_VERSION}") if(EXISTS "${POSSIBLE_DIR}") set(SOAPY_SDR_MODULE_DIR "${POSSIBLE_DIR}") break() endif() endforeach() if(NOT SOAPY_SDR_MODULE_DIR) set(SOAPY_SDR_MODULE_DIR "/usr/local/lib/SoapySDR/modules${SoapySDR_ABI_VERSION}") endif() else() # Linux - try common locations with both ABI versions # Order: source-built paths first (0.8-3), then system package paths (0.8) foreach(POSSIBLE_DIR "/usr/local/lib/SoapySDR/modules${SoapySDR_ABI_VERSION}" "/usr/local/lib/SoapySDR/modules0.8-3" "/usr/local/lib/SoapySDR/modules0.8" "/usr/lib/x86_64-linux-gnu/SoapySDR/modules${SoapySDR_ABI_VERSION}" "/usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8-3" "/usr/lib/x86_64-linux-gnu/SoapySDR/modules0.8" "/usr/lib/SoapySDR/modules${SoapySDR_ABI_VERSION}" "/usr/lib64/SoapySDR/modules${SoapySDR_ABI_VERSION}") if(EXISTS "${POSSIBLE_DIR}") set(SOAPY_SDR_MODULE_DIR "${POSSIBLE_DIR}") break() endif() endforeach() # Final fallback - use detected ABI version with /usr/local prefix if(NOT SOAPY_SDR_MODULE_DIR) set(SOAPY_SDR_MODULE_DIR "/usr/local/lib/SoapySDR/modules${SoapySDR_ABI_VERSION}") endif() endif() message(STATUS "SoapySDR module directory not auto-detected, using fallback: ${SOAPY_SDR_MODULE_DIR}") endif() endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # Try to find system-installed LibHYDRASDR first find_package(LibHYDRASDR REQUIRED) if(NOT LibHYDRASDR_FOUND) message(STATUS "System LibHYDRASDR not found, will use locally built version") # Set paths for locally built LibHYDRASDR # Check both source dir (CI builds here) and binary dir (local builds) set(POSSIBLE_INSTALL_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/rfone_host_build/install" "${CMAKE_CURRENT_BINARY_DIR}/rfone_host_build/install" "${CMAKE_CURRENT_SOURCE_DIR}/../rfone_host_build/install" ) foreach(INSTALL_DIR ${POSSIBLE_INSTALL_DIRS}) if(WIN32) set(TEST_LIB "${INSTALL_DIR}/lib/hydrasdr.lib") set(TEST_HEADER "${INSTALL_DIR}/include/libhydrasdr/hydrasdr.h") elseif(APPLE) set(TEST_LIB "${INSTALL_DIR}/lib/libhydrasdr.dylib") set(TEST_HEADER "${INSTALL_DIR}/include/libhydrasdr/hydrasdr.h") else() # Linux set(TEST_LIB "${INSTALL_DIR}/lib/libhydrasdr.so") set(TEST_HEADER "${INSTALL_DIR}/include/libhydrasdr/hydrasdr.h") endif() # Also check alternative header location set(ALT_HEADER "${INSTALL_DIR}/include/hydrasdr.h") if(EXISTS "${TEST_LIB}" AND (EXISTS "${TEST_HEADER}" OR EXISTS "${ALT_HEADER}")) set(LibHYDRASDR_INCLUDE_DIRS "${INSTALL_DIR}/include") set(LibHYDRASDR_LIBRARIES "${TEST_LIB}") message(STATUS "Found locally built LibHydraSDR at: ${INSTALL_DIR}") break() endif() endforeach() # Verify we found the library if(NOT LibHYDRASDR_LIBRARIES OR NOT LibHYDRASDR_INCLUDE_DIRS) message(FATAL_ERROR "Could not find locally built LibHydraSDR. Please ensure it is built first. Searched in these locations: ${POSSIBLE_INSTALL_DIRS} Expected files: - Library: ${TEST_LIB} - Header: ${TEST_HEADER} or ${ALT_HEADER}") endif() endif() message(STATUS "LibHYDRASDR include dirs: ${LibHYDRASDR_INCLUDE_DIRS}") message(STATUS "LibHYDRASDR libraries: ${LibHYDRASDR_LIBRARIES}") # Include directories - handle both direct include and subdirectory cases if(LibHYDRASDR_INCLUDE_DIRS) # If headers are found in libhydrasdr subdirectory, we need to include the parent directory # so that #include works if("${LibHYDRASDR_INCLUDE_DIRS}" MATCHES "libhydrasdr$") # Headers are in libhydrasdr subdirectory, include the parent get_filename_component(PARENT_INCLUDE_DIR "${LibHYDRASDR_INCLUDE_DIRS}" DIRECTORY) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${PARENT_INCLUDE_DIR}) message(STATUS "Including parent directory for libhydrasdr headers: ${PARENT_INCLUDE_DIR}") else() # Headers are in main include directory include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${LibHYDRASDR_INCLUDE_DIRS}) message(STATUS "Including directory: ${LibHYDRASDR_INCLUDE_DIRS}") endif() else() message(FATAL_ERROR "LibHYDRASDR include directories not found") endif() # Enable C++11 features if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # C++11 is a required language feature for this project include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_STD_CXX11) if(HAS_STD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") else(HAS_STD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") endif() # Thread support enabled (not the same as -lpthread) list(APPEND HYDRASDR_LIBS -pthread) # Compiler warnings (Linux Kernel style preferences) add_compile_options(-Wall -Wextra -Wno-unused-parameter) # Additional useful warnings add_compile_options(-Wformat=2 -Wformat-security -Wformat-nonliteral) add_compile_options(-Wcast-qual -Wcast-align) add_compile_options(-Wpointer-arith -Wredundant-decls) endif() if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wc++11-extensions") endif(APPLE) if(WIN32 AND MSVC) # Windows-specific compiler settings add_compile_options(/W4 /WX-) # High warning level, but don't treat warnings as errors add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS) # Disable specific MSVC warnings that are too noisy add_compile_options(/wd4127) # conditional expression is constant add_compile_options(/wd4996) # deprecated functions endif() # Platform-specific libraries if(APPLE) find_library(COREFOUNDATION_LIBRARY CoreFoundation) list(APPEND HYDRASDR_LIBS ${COREFOUNDATION_LIBRARY}) endif() list(APPEND HYDRASDR_LIBS ${LibHYDRASDR_LIBRARIES}) # Verify paths exist and provide detailed information if(LibHYDRASDR_LIBRARIES) message(STATUS "LibHydraSDR library for linking: ${LibHYDRASDR_LIBRARIES}") if(IS_ABSOLUTE "${LibHYDRASDR_LIBRARIES}") message(STATUS " ✓ Library path is absolute") else() message(STATUS " ✗ Library path is relative - this may cause linking issues") endif() if(EXISTS "${LibHYDRASDR_LIBRARIES}") message(STATUS " ✓ Library file exists") else() message(FATAL_ERROR " ✗ Library file does NOT exist: ${LibHYDRASDR_LIBRARIES}") endif() endif() # Verify include directory exists if(LibHYDRASDR_INCLUDE_DIRS) if(EXISTS "${LibHYDRASDR_INCLUDE_DIRS}") message(STATUS " ✓ Include directory exists") # Check for header files if(EXISTS "${LibHYDRASDR_INCLUDE_DIRS}/libhydrasdr/hydrasdr.h") message(STATUS " ✓ Found hydrasdr.h in libhydrasdr subdirectory") elseif(EXISTS "${LibHYDRASDR_INCLUDE_DIRS}/hydrasdr.h") message(STATUS " ✓ Found hydrasdr.h in root include directory") else() message(FATAL_ERROR " ✗ Could not find hydrasdr.h in include directory: ${LibHYDRASDR_INCLUDE_DIRS}") endif() else() message(FATAL_ERROR " ✗ Include directory does NOT exist: ${LibHYDRASDR_INCLUDE_DIRS}") endif() endif() # Create the SoapySDR module SOAPY_SDR_MODULE_UTIL( TARGET SoapyHydraSDR SOURCES SoapyHydraSDR.hpp Registration.cpp Settings.cpp Streaming.cpp LIBRARIES ${HYDRASDR_LIBS} ) # Set target properties set_target_properties(SoapyHydraSDR PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON VERSION ${SOAPYHYDRASDR_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) # Fix macOS compatibility version issue if(APPLE) set_target_properties(SoapyHydraSDR PROPERTIES MACOSX_RPATH ON INSTALL_RPATH_USE_LINK_PATH ON # Remove problematic version properties for SoapySDR modules VERSION "" SOVERSION "" ) endif() # Add version compile definition target_compile_definitions(SoapyHydraSDR PRIVATE SOAPYHYDRASDR_VERSION="${SOAPYHYDRASDR_VERSION}" ) # Ensure proper include paths for the target if(LibHYDRASDR_INCLUDE_DIRS) # If headers are found in libhydrasdr subdirectory, we need to include the parent directory if("${LibHYDRASDR_INCLUDE_DIRS}" MATCHES "libhydrasdr$") # Headers are in libhydrasdr subdirectory, include the parent get_filename_component(PARENT_INCLUDE_DIR "${LibHYDRASDR_INCLUDE_DIRS}" DIRECTORY) target_include_directories(SoapyHydraSDR PRIVATE ${PARENT_INCLUDE_DIR}) message(STATUS "Adding parent include directory to target: ${PARENT_INCLUDE_DIR}") else() # Headers are in main include directory target_include_directories(SoapyHydraSDR PRIVATE ${LibHYDRASDR_INCLUDE_DIRS}) message(STATUS "Adding include directory to target: ${LibHYDRASDR_INCLUDE_DIRS}") endif() endif() # Installation rules install(TARGETS SoapyHydraSDR LIBRARY DESTINATION ${SOAPY_SDR_MODULE_DIR} ARCHIVE DESTINATION ${SOAPY_SDR_MODULE_DIR} RUNTIME DESTINATION ${SOAPY_SDR_MODULE_DIR} ) # Verify module directory before installation if(NOT SOAPY_SDR_MODULE_DIR) message(FATAL_ERROR "SoapySDR module directory not found. Cannot install module.") endif() message(STATUS "Will install SoapySDR module to: ${SOAPY_SDR_MODULE_DIR}") # On Windows, also install required DLLs if(WIN32) # Use the same path calculation approach for consistency get_filename_component(PROJECT_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}" DIRECTORY) set(RFONE_BUILD_DIR "${PROJECT_ROOT_DIR}/rfone_host_build/install") if(EXISTS "${RFONE_BUILD_DIR}/bin/hydrasdr.dll") install(FILES "${RFONE_BUILD_DIR}/bin/hydrasdr.dll" DESTINATION ${SOAPY_SDR_MODULE_DIR}) message(STATUS "Will install hydrasdr.dll from: ${RFONE_BUILD_DIR}/bin/") endif() # Install other required Windows DLLs if they exist file(GLOB WINDOWS_DLLS "${RFONE_BUILD_DIR}/bin/*.dll") if(WINDOWS_DLLS) install(FILES ${WINDOWS_DLLS} DESTINATION ${SOAPY_SDR_MODULE_DIR}) message(STATUS "Will install additional DLLs: ${WINDOWS_DLLS}") endif() endif() # Print build information message(STATUS "") message(STATUS "Build Configuration Summary:") message(STATUS " Project Version: ${SOAPYHYDRASDR_VERSION}") message(STATUS " Build Type: ${CMAKE_BUILD_TYPE}") message(STATUS " Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS " Install Prefix: ${CMAKE_INSTALL_PREFIX}") message(STATUS " SoapySDR Version: ${SoapySDR_VERSION}") message(STATUS " SoapySDR ABI Version: ${SoapySDR_ABI_VERSION}") message(STATUS " SoapySDR Module Dir: ${SOAPY_SDR_MODULE_DIR}") if(LibHYDRASDR_FOUND) message(STATUS " LibHYDRASDR Found: YES") else() message(STATUS " LibHYDRASDR Found: NO") endif() message(STATUS " LibHYDRASDR Include: ${LibHYDRASDR_INCLUDE_DIRS}") message(STATUS " LibHYDRASDR Libraries: ${LibHYDRASDR_LIBRARIES}") # Debug: Show what will actually be used for compilation if(LibHYDRASDR_INCLUDE_DIRS) if("${LibHYDRASDR_INCLUDE_DIRS}" MATCHES "libhydrasdr$") get_filename_component(ACTUAL_INCLUDE_DIR "${LibHYDRASDR_INCLUDE_DIRS}" DIRECTORY) message(STATUS " Actual Include Dir for Compilation: ${ACTUAL_INCLUDE_DIR}") else() message(STATUS " Actual Include Dir for Compilation: ${LibHYDRASDR_INCLUDE_DIRS}") endif() endif() if(WIN32) message(STATUS " MSVC Runtime: ${CMAKE_MSVC_RUNTIME_LIBRARY}") endif() message(STATUS "") hydrasdr-SoapyHydraSDR-253ce50/Changelog.txt000066400000000000000000000001521512157426400207510ustar00rootroot00000000000000Release 0.1.0 (2025-06-14) ========================== - Initial release of Soapy HydraSDR support module hydrasdr-SoapyHydraSDR-253ce50/LICENSE.txt000066400000000000000000000020741512157426400201510ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Charles J. Cliffe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hydrasdr-SoapyHydraSDR-253ce50/LibFindMacros.cmake000066400000000000000000000100421512157426400217760ustar00rootroot00000000000000# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments # used for the current package. For this to work, the first parameter must be the # prefix of the current package, then the prefix of the new package etc, which are # passed to find_package. macro (libfind_package PREFIX) set (LIBFIND_PACKAGE_ARGS ${ARGN}) if (${PREFIX}_FIND_QUIETLY) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) endif (${PREFIX}_FIND_QUIETLY) if (${PREFIX}_FIND_REQUIRED) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) endif (${PREFIX}_FIND_REQUIRED) find_package(${LIBFIND_PACKAGE_ARGS}) endmacro (libfind_package) # CMake developers made the UsePkgConfig system deprecated in the same release (2.6) # where they added pkg_check_modules. Consequently I need to support both in my scripts # to avoid those deprecated warnings. Here's a helper that does just that. # Works identically to pkg_check_modules, except that no checks are needed prior to use. macro (libfind_pkg_check_modules PREFIX PKGNAME) if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) include(UsePkgConfig) pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(${PREFIX} ${PKGNAME}) endif (PKG_CONFIG_FOUND) endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) endmacro (libfind_pkg_check_modules) # Do the final processing once the paths have been detected. # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain # all the variables, each of which contain one include directory. # Ditto for ${PREFIX}_PROCESS_LIBS and library files. # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. # Also handles errors in case library detection was required, etc. macro (libfind_process PREFIX) # Skip processing if already processed during this run if (NOT ${PREFIX}_FOUND) # Start with the assumption that the library was found set (${PREFIX}_FOUND TRUE) # Process all includes and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_INCLUDES}) if (${i}) set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Process all libraries and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_LIBS}) if (${i}) set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Print message and/or exit on fatal error if (${PREFIX}_FOUND) if (NOT ${PREFIX}_FIND_QUIETLY) message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") endif (NOT ${PREFIX}_FIND_QUIETLY) else (${PREFIX}_FOUND) if (${PREFIX}_FIND_REQUIRED) foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) message("${i}=${${i}}") endforeach (i) message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") endif (${PREFIX}_FIND_REQUIRED) endif (${PREFIX}_FOUND) endif (NOT ${PREFIX}_FOUND) endmacro (libfind_process) macro(libfind_library PREFIX basename) set(TMP "") if(MSVC80) set(TMP -vc80) endif(MSVC80) if(MSVC90) set(TMP -vc90) endif(MSVC90) set(${PREFIX}_LIBNAMES ${basename}${TMP}) if(${ARGC} GREATER 2) set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) endif(${ARGC} GREATER 2) find_library(${PREFIX}_LIBRARY NAMES ${${PREFIX}_LIBNAMES} PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} ) endmacro(libfind_library) hydrasdr-SoapyHydraSDR-253ce50/README.md000066400000000000000000000331461512157426400176110ustar00rootroot00000000000000# SoapySDR Plugin for HydraSDR SoapySDR driver module for HydraSDR RFOne software-defined radio hardware. Provides seamless integration with the SoapySDR ecosystem and applications like GQRX, CubicSDR, and any Soapy-compatible SDR application across Windows, Linux, and macOS ## Dependencies * **SoapySDR** - https://github.com/pothosware/SoapySDR * **libhydrasdr** - https://github.com/hydrasdr/rfone_host ## Installation ### Option 1: Pre-built Packages (Recommended) Pre-built packages are available for multiple platforms. You need to install three components: 1. **SoapySDR** - The SDR abstraction library 2. **hydrasdr-host-tools** - HydraSDR host library and tools (provides libhydrasdr) 3. **soapyhydrasdr** - This SoapySDR module **Download packages from:** - **hydrasdr-host-tools**: https://github.com/hydrasdr/rfone_host/releases - **soapyhydrasdr**: https://github.com/hydrasdr/SoapyHydraSDR/releases #### Debian / Ubuntu / Linux Mint ```bash # Step 1: Install SoapySDR from distribution packages sudo apt-get update sudo apt-get install -y libsoapysdr0.8 libsoapysdr-dev soapysdr-tools # Step 2: Download and install hydrasdr-host-tools # Download the appropriate .deb for your distro from: https://github.com/hydrasdr/rfone_host/releases sudo dpkg -i hydrasdr-host-tools--*.deb sudo ldconfig # Step 3: Download and install soapyhydrasdr # Download the appropriate .deb for your distro from: https://github.com/hydrasdr/SoapyHydraSDR/releases sudo dpkg -i soapyhydrasdr--*.deb sudo ldconfig # Step 4: Reload udev rules and verify sudo udevadm control --reload-rules sudo udevadm trigger SoapySDRUtil --info SoapySDRUtil --find ``` **Available .deb packages:** | Distribution | Package name pattern | |--------------|----------------------| | Ubuntu 24.04 LTS | `*-Ubuntu-24.04-LTS-Noble-Numbat-*.deb` | | Ubuntu 22.04 LTS | `*-Ubuntu-22.04-LTS-Jammy-Jellyfish-*.deb` | | Ubuntu 20.04 LTS | `*-Ubuntu-20.04-LTS-Focal-Fossa-*.deb` | | Debian 13 Trixie | `*-Debian-13-Trixie-*.deb` | | Debian 12 Bookworm | `*-Debian-12-Bookworm-*.deb` | | Debian 11 Bullseye | `*-Debian-11-Bullseye-*.deb` | | Linux Mint 22 | `*-Linux-Mint-22-Wilma-*.deb` | | Linux Mint 21.x | `*-Linux-Mint-21.*-*.deb` | #### Fedora ```bash # Step 1: Install SoapySDR from Fedora repositories sudo dnf install -y SoapySDR SoapySDR-devel soapysdr-tools # Step 2: Download and install hydrasdr-host-tools # Download the appropriate .rpm for your Fedora version from: https://github.com/hydrasdr/rfone_host/releases sudo dnf install ./hydrasdr-host-tools-Fedora-*.rpm # Step 3: Download and install soapyhydrasdr # Download the appropriate .rpm for your Fedora version from: https://github.com/hydrasdr/SoapyHydraSDR/releases sudo dnf install ./soapyhydrasdr-Fedora-*.rpm # Step 4: Reload udev rules and verify sudo udevadm control --reload-rules sudo udevadm trigger SoapySDRUtil --info SoapySDRUtil --find ``` **Available Fedora .rpm packages:** `*-Fedora-41-*.rpm`, `*-Fedora-42-*.rpm` #### openSUSE Tumbleweed ```bash # Step 1: Install SoapySDR from openSUSE repositories sudo zypper install soapy-sdr soapy-sdr-devel # Step 2: Download and install hydrasdr-host-tools # Download the .rpm from: https://github.com/hydrasdr/rfone_host/releases sudo zypper install ./hydrasdr-host-tools-openSUSE-Tumbleweed-*.rpm # Step 3: Download and install soapyhydrasdr # Download the .rpm from: https://github.com/hydrasdr/SoapyHydraSDR/releases sudo zypper install ./soapyhydrasdr-openSUSE-Tumbleweed-*.rpm # Step 4: Reload udev rules and verify sudo udevadm control --reload-rules sudo udevadm trigger SoapySDRUtil --info SoapySDRUtil --find ``` #### AlmaLinux 9 / RHEL 9 ```bash # Step 1: Enable EPEL and install SoapySDR sudo dnf install -y epel-release sudo dnf config-manager --set-enabled crb sudo dnf install -y SoapySDR SoapySDR-devel # Step 2: Download and install hydrasdr-host-tools # Download the .rpm from: https://github.com/hydrasdr/rfone_host/releases sudo dnf install ./hydrasdr-host-tools-AlmaLinux-9-*.rpm # Step 3: Download and install soapyhydrasdr # Download the .rpm from: https://github.com/hydrasdr/SoapyHydraSDR/releases sudo dnf install ./soapyhydrasdr-AlmaLinux-9-*.rpm # Step 4: Reload udev rules and verify sudo udevadm control --reload-rules sudo udevadm trigger SoapySDRUtil --info SoapySDRUtil --find ``` #### macOS ```bash # Step 1: Install SoapySDR via Homebrew brew install soapysdr # Step 2: Download and extract hydrasdr-host-tools # Download from: https://github.com/hydrasdr/rfone_host/releases # - Apple Silicon (M1/M2/M3): hydrasdr-host-tools-macOS-ARM64-*.tar.gz # - Intel Mac: hydrasdr-host-tools-macOS-x86_64-*.tar.gz tar -xzf hydrasdr-host-tools-macOS-*.tar.gz sudo cp libhydrasdr.dylib /usr/local/lib/ sudo cp hydrasdr_* /usr/local/bin/ # optional: command-line tools # Step 3: Download and extract soapyhydrasdr # Download from: https://github.com/hydrasdr/SoapyHydraSDR/releases # - Apple Silicon (M1/M2/M3): soapyhydrasdr-macOS-ARM64-*.tar.gz # - Intel Mac: soapyhydrasdr-macOS-x86_64-*.tar.gz tar -xzf soapyhydrasdr-macOS-*.tar.gz # Find SoapySDR modules directory and install SOAPY_MODULE_DIR=$(SoapySDRUtil --info | grep "Search path" | head -1 | awk '{print $3}') sudo mkdir -p "$SOAPY_MODULE_DIR" sudo cp libSoapyHydraSDR.so "$SOAPY_MODULE_DIR/" # Step 4: Verify installation SoapySDRUtil --info SoapySDRUtil --find ``` #### Windows 1. **Install SoapySDR** via PothosSDR installer or vcpkg: - **PothosSDR** (includes SoapySDR): https://github.com/pothosware/PothosSDR/releases - Or via vcpkg: `vcpkg install soapysdr:x64-windows` 2. **Download hydrasdr-host-tools**: - Download `hydrasdr-host-tools-Windows-x64-*.zip` from: https://github.com/hydrasdr/rfone_host/releases - Extract the archive 3. **Download soapyhydrasdr**: - Download `soapyhydrasdr-Windows-x64-*.zip` from: https://github.com/hydrasdr/SoapyHydraSDR/releases - Extract the archive 4. **Install files**: ```powershell # Find SoapySDR modules directory (check SoapySDRUtil --info) # Typical locations: # C:\Program Files\PothosSDR\lib\SoapySDR\modules0.8\ # C:\Program Files\SoapySDR\lib\SoapySDR\modules0.8\ # Copy SoapyHydraSDR.dll to SoapySDR modules directory Copy-Item SoapyHydraSDR.dll "C:\Program Files\PothosSDR\lib\SoapySDR\modules0.8\" # Copy hydrasdr.dll and dependencies to the modules directory or add to PATH Copy-Item hydrasdr.dll "C:\Program Files\PothosSDR\lib\SoapySDR\modules0.8\" Copy-Item libusb-1.0.dll "C:\Program Files\PothosSDR\lib\SoapySDR\modules0.8\" ``` 5. **Verify installation**: ```powershell SoapySDRUtil --info SoapySDRUtil --find ``` **Note:** HydraSDR RFOne uses WCID for automatic USB driver installation on Windows 10/11. ### Option 2: Build from Source #### Linux/macOS For best compatibility, especially if you've built SoapySDR from source, build SoapyHydraSDR from source as well: ```bash # Install dependencies (Debian/Ubuntu) sudo apt-get install build-essential cmake pkg-config libusb-1.0-0-dev # Optional: Clean up previous installations sudo rm -f /usr/local/lib/libhydrasdr* sudo rm -f /usr/local/lib/SoapySDR/modules*/libSoapyHydraSDR* sudo ldconfig # Build and install SoapySDR from source (recommended) rm -rf SoapySDR git clone https://github.com/pothosware/SoapySDR.git cd SoapySDR && mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j$(nproc) sudo make install sudo ldconfig cd ../.. # Build and install libhydrasdr (shared library required) rm -rf rfone_host git clone https://github.com/hydrasdr/rfone_host.git cd rfone_host && mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_SHARED_LIB=ON -DINSTALL_UDEV_RULES=ON make -j$(nproc) sudo make install sudo ldconfig cd ../.. # Build and install SoapyHydraSDR rm -rf SoapyHydraSDR git clone https://github.com/hydrasdr/SoapyHydraSDR.git cd SoapyHydraSDR && mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j$(nproc) sudo make install sudo ldconfig # Verify installation SoapySDRUtil --info SoapySDRUtil --probe="driver=hydrasdr" ``` #### Windows Building on Windows requires Visual Studio and vcpkg: ```powershell # Install vcpkg (if not already installed) git clone https://github.com/Microsoft/vcpkg.git C:\vcpkg C:\vcpkg\bootstrap-vcpkg.bat # Install dependencies C:\vcpkg\vcpkg.exe install soapysdr:x64-windows libusb:x64-windows # Clone and build libhydrasdr git clone https://github.com/hydrasdr/rfone_host.git cd rfone_host mkdir build && cd build cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake -DENABLE_SHARED_LIB=ON cmake --build . --config Release cmake --install . --config Release cd ..\.. # Clone and build SoapyHydraSDR git clone https://github.com/hydrasdr/SoapyHydraSDR.git cd SoapyHydraSDR mkdir build && cd build cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE=C:\vcpkg\scripts\buildsystems\vcpkg.cmake cmake --build . --config Release cmake --install . --config Release ``` ## ABI Compatibility Note **Important:** SoapySDR uses an ABI (Application Binary Interface) version to ensure module compatibility. The pre-built soapyhydrasdr packages install to multiple module paths for maximum compatibility with both distribution packages and source-built SoapySDR. If you experience module loading errors like: ``` SoapySDR module ... refuses to load. Check module file permissions and ABI version ``` This typically means there's a mismatch between your installed SoapySDR and the module. Solutions: 1. **Build SoapyHydraSDR from source** (see Option 2) - This ensures the module is compiled against your exact SoapySDR version. 2. **Build SoapySDR from source** - If distribution packages cause issues: ```bash git clone https://github.com/pothosware/SoapySDR.git cd SoapySDR && mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j$(nproc) sudo make install sudo ldconfig ``` ## Verification After installation, verify the module is detected: ```bash # Check if SoapySDR can find the module SoapySDRUtil --info # Look for HydraSDR devices SoapySDRUtil --find # Probe device details (with device connected) SoapySDRUtil --probe="driver=hydrasdr" ``` ## Troubleshooting ### Module not found 1. Check the SoapySDR module search paths: ```bash SoapySDRUtil --info | grep "Search path" ``` 2. Verify the module is installed in one of those paths: ```bash ls -la /usr/local/lib/SoapySDR/modules*/ ls -la /usr/lib/x86_64-linux-gnu/SoapySDR/modules*/ ``` ### ABI version mismatch Build both SoapySDR and SoapyHydraSDR from source to ensure ABI compatibility. See the build instructions above. ### Device not detected 1. Ensure the device is connected via USB 2. Check USB permissions - udev rules must be installed: **If using pre-built packages:** udev rules are included automatically. Just reload and replug: ```bash sudo udevadm control --reload-rules sudo udevadm trigger ``` **If built from source:** ensure you built libhydrasdr with udev rules enabled: ```bash cd rfone_host/build cmake .. -DINSTALL_UDEV_RULES=ON make sudo make install sudo udevadm control --reload-rules sudo udevadm trigger ``` 3. Verify udev rules are installed: ```bash ls -la /etc/udev/rules.d/*hydrasdr* ``` 4. Re-plug the device or log out/in ### Permission denied (Linux) Add your user to the `plugdev` group: ```bash sudo usermod -aG plugdev $USER # Log out and log back in ``` ### Windows: Device not recognized 1. HydraSDR RFOne uses WCID for automatic driver installation 2. Ensure the device is properly connected via USB 3. Check Device Manager for any driver issues 4. Verify with `SoapySDRUtil --find` ### Windows: Module not loaded 1. Ensure all DLLs are in the SoapySDR modules directory or in PATH 2. Check the module path: `SoapySDRUtil --info` 3. Typical paths: - `C:\Program Files\PothosSDR\lib\SoapySDR\modules0.8\` - `C:\Program Files\SoapySDR\lib\SoapySDR\modules0.8\` - `C:\vcpkg\installed\x64-windows\lib\SoapySDR\modules0.8\` ## Device Capabilities Example output from `SoapySDRUtil --probe="driver=hydrasdr"`: ``` ###################################################### ## Soapy SDR -- the SDR abstraction library ## ###################################################### Probe device driver=hydrasdr ---------------------------------------------------- -- Device identification ---------------------------------------------------- driver=HydraSDR hardware=HydraSDR serial=36b463dc395884c7 ---------------------------------------------------- -- Peripheral summary ---------------------------------------------------- Channels: 1 Rx, 0 Tx Timestamps: NO Other Settings: * Bias tee - Enable the 4.5v DC Bias tee to power LNA / etc. via antenna connection. [key=biastee, default=false, type=bool] * Bit Pack - Enable packing 4 12-bit samples into 3 16-bit words for 25% less USB traffic. [key=bitpack, default=false, type=bool] ---------------------------------------------------- -- RX Channel 0 ---------------------------------------------------- Full-duplex: NO Supports AGC: YES Stream formats: CS16, CF32 Native format: CS16 [full-scale=32767] Antennas: RX Full gain range: [0, 45] dB LNA gain range: [0, 15] dB MIX gain range: [0, 15] dB VGA gain range: [0, 15] dB Full freq range: [24, 1800] MHz RF freq range: [24, 1800] MHz Sample rates: 10, 5, 2.5 MSps ``` ## License This software is licensed exclusively for HydraSDR products and related development. See [LICENSE.txt](LICENSE.txt) for details. ## Links * **HydraSDR**: https://hydrasdr.com * **Issue Tracker**: https://github.com/hydrasdr/SoapyHydraSDR/issues * **SoapySDR Documentation**: https://github.com/pothosware/SoapySDR/wiki hydrasdr-SoapyHydraSDR-253ce50/Registration.cpp000066400000000000000000000054571512157426400215140ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "SoapyHydraSDR.hpp" #include #include //malloc #include static std::vector findHydraSDR(const SoapySDR::Kwargs &args) { std::vector results; hydrasdr_lib_version_t asVersion; hydrasdr_lib_version(&asVersion); SoapySDR_logf(SOAPY_SDR_DEBUG, "HydraSDR Lib v%d.%d rev %d", asVersion.major_version, asVersion.minor_version, asVersion.revision); uint64_t serials[MAX_DEVICES]; int count = hydrasdr_list_devices(serials, MAX_DEVICES); if (count < 0) { SoapySDR_logf(SOAPY_SDR_ERROR, "libhydrasdr error listing devices"); return results; } SoapySDR_logf(SOAPY_SDR_DEBUG, "%d HydraSDR boards found.", count); for (int i = 0; i < count; i++) { std::stringstream serialstr; serialstr.str(""); serialstr << std::hex << serials[i]; SoapySDR_logf(SOAPY_SDR_DEBUG, "Serial %s", serialstr.str().c_str()); SoapySDR::Kwargs soapyInfo; soapyInfo["label"] = "HydraSDR RFOne [" + serialstr.str() + "]"; soapyInfo["serial"] = serialstr.str(); if (args.count("serial") != 0) { if (args.at("serial") != soapyInfo.at("serial")) { continue; } SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by serial %s", soapyInfo.at("serial").c_str()); } results.push_back(soapyInfo); } return results; } static SoapySDR::Device *makeHydraSDR(const SoapySDR::Kwargs &args) { return new SoapyHydraSDR(args); } static SoapySDR::Registry registerHydraSDR("hydrasdr", &findHydraSDR, &makeHydraSDR, SOAPY_SDR_ABI_VERSION); hydrasdr-SoapyHydraSDR-253ce50/Settings.cpp000066400000000000000000000265331512157426400206400ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "SoapyHydraSDR.hpp" #include SoapyHydraSDR::SoapyHydraSDR(const SoapySDR::Kwargs &args) { sampleRate = 3000000; centerFrequency = 0; numBuffers = DEFAULT_NUM_BUFFERS; agcMode = false; rfBias = false; bitPack = false; bufferedElems = 0; resetBuffer = false; streamActive = false; sampleRateChanged.store(false); dev = nullptr; lnaGain = mixerGain = vgaGain = 0; dev = nullptr; std::stringstream serialstr; serialstr.str(""); if (args.count("serial") != 0) { try { serial = std::stoull(args.at("serial"), nullptr, 16); } catch (const std::invalid_argument &) { throw std::runtime_error("serial is not a hex number"); } catch (const std::out_of_range &) { throw std::runtime_error("serial value of out range"); } serialstr << std::hex << serial; if (hydrasdr_open_sn(&dev, serial) != HYDRASDR_SUCCESS) { throw std::runtime_error("Unable to open HydraSDR device with serial " + serialstr.str()); } SoapySDR_logf(SOAPY_SDR_DEBUG, "Found HydraSDR device: serial = %" PRIx64, serial); } else { if (hydrasdr_open(&dev) != HYDRASDR_SUCCESS) { throw std::runtime_error("Unable to open HydraSDR device"); } } //apply arguments to settings when they match for (const auto &info : this->getSettingInfo()) { const auto it = args.find(info.key); if (it != args.end()) this->writeSetting(it->first, it->second); } } SoapyHydraSDR::~SoapyHydraSDR(void) { hydrasdr_close(dev); } /******************************************************************* * Identification API ******************************************************************/ std::string SoapyHydraSDR::getDriverKey(void) const { return "HydraSDR"; } std::string SoapyHydraSDR::getHardwareKey(void) const { return "HydraSDR"; } SoapySDR::Kwargs SoapyHydraSDR::getHardwareInfo(void) const { //key/value pairs for any useful information //this also gets printed in --probe SoapySDR::Kwargs args; std::stringstream serialstr; serialstr.str(""); serialstr << std::hex << serial; args["serial"] = serialstr.str(); return args; } /******************************************************************* * Channels API ******************************************************************/ size_t SoapyHydraSDR::getNumChannels(const int dir) const { return (dir == SOAPY_SDR_RX) ? 1 : 0; } /******************************************************************* * Antenna API ******************************************************************/ std::vector SoapyHydraSDR::listAntennas(const int direction, const size_t channel) const { std::vector antennas; antennas.push_back("RX"); return antennas; } void SoapyHydraSDR::setAntenna(const int direction, const size_t channel, const std::string &name) { // TODO } std::string SoapyHydraSDR::getAntenna(const int direction, const size_t channel) const { return "RX"; } /******************************************************************* * Frontend corrections API ******************************************************************/ bool SoapyHydraSDR::hasDCOffsetMode(const int direction, const size_t channel) const { return false; } /******************************************************************* * Gain API ******************************************************************/ std::vector SoapyHydraSDR::listGains(const int direction, const size_t channel) const { //list available gain elements, //the functions below have a "name" parameter std::vector results; results.push_back("LNA"); results.push_back("MIX"); results.push_back("VGA"); return results; } bool SoapyHydraSDR::hasGainMode(const int direction, const size_t channel) const { return true; } void SoapyHydraSDR::setGainMode(const int direction, const size_t channel, const bool automatic) { agcMode = automatic; hydrasdr_set_lna_agc(dev, agcMode?1:0); hydrasdr_set_mixer_agc(dev, agcMode?1:0); SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting AGC: %s", automatic ? "Automatic" : "Manual"); } bool SoapyHydraSDR::getGainMode(const int direction, const size_t channel) const { return agcMode; } void SoapyHydraSDR::setGain(const int direction, const size_t channel, const double value) { //set the overall gain by distributing it across available gain elements //OR delete this function to use SoapySDR's default gain distribution algorithm... SoapySDR::Device::setGain(direction, channel, value); } void SoapyHydraSDR::setGain(const int direction, const size_t channel, const std::string &name, const double value) { if (name == "LNA") { lnaGain = uint8_t(value); hydrasdr_set_lna_gain(dev, lnaGain); } else if (name == "MIX") { mixerGain = uint8_t(value); hydrasdr_set_mixer_gain(dev, mixerGain); } else if (name == "VGA") { vgaGain = uint8_t(value); hydrasdr_set_vga_gain(dev, vgaGain); } } double SoapyHydraSDR::getGain(const int direction, const size_t channel, const std::string &name) const { if (name == "LNA") { return lnaGain; } else if (name == "MIX") { return mixerGain; } else if (name == "VGA") { return vgaGain; } return 0; } SoapySDR::Range SoapyHydraSDR::getGainRange(const int direction, const size_t channel, const std::string &name) const { if (name == "LNA" || name == "MIX" || name == "VGA") { return SoapySDR::Range(0, 15); } return SoapySDR::Range(0, 15); } /******************************************************************* * Frequency API ******************************************************************/ void SoapyHydraSDR::setFrequency( const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args) { if (name == "RF") { centerFrequency = (uint32_t) frequency; resetBuffer = true; SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting center freq: %d", centerFrequency); hydrasdr_set_freq(dev, centerFrequency); } } double SoapyHydraSDR::getFrequency(const int direction, const size_t channel, const std::string &name) const { if (name == "RF") { return (double) centerFrequency; } return 0; } std::vector SoapyHydraSDR::listFrequencies(const int direction, const size_t channel) const { std::vector names; names.push_back("RF"); return names; } SoapySDR::RangeList SoapyHydraSDR::getFrequencyRange( const int direction, const size_t channel, const std::string &name) const { SoapySDR::RangeList results; if (name == "RF") { results.push_back(SoapySDR::Range(24000000, 1800000000)); } return results; } SoapySDR::ArgInfoList SoapyHydraSDR::getFrequencyArgsInfo(const int direction, const size_t channel) const { SoapySDR::ArgInfoList freqArgs; // TODO: frequency arguments return freqArgs; } /******************************************************************* * Sample Rate API ******************************************************************/ void SoapyHydraSDR::setSampleRate(const int direction, const size_t channel, const double rate) { SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting sample rate: %d", sampleRate); if (sampleRate != rate) { sampleRate = rate; resetBuffer = true; sampleRateChanged.store(true); } } double SoapyHydraSDR::getSampleRate(const int direction, const size_t channel) const { return sampleRate; } std::vector SoapyHydraSDR::listSampleRates(const int direction, const size_t channel) const { std::vector results; uint32_t numRates; hydrasdr_get_samplerates(dev, &numRates, 0); std::vector samplerates; samplerates.resize(numRates); hydrasdr_get_samplerates(dev, samplerates.data(), numRates); for (auto i: samplerates) { results.push_back(i); } return results; } void SoapyHydraSDR::setBandwidth(const int direction, const size_t channel, const double bw) { SoapySDR::Device::setBandwidth(direction, channel, bw); } double SoapyHydraSDR::getBandwidth(const int direction, const size_t channel) const { return SoapySDR::Device::getBandwidth(direction, channel); } std::vector SoapyHydraSDR::listBandwidths(const int direction, const size_t channel) const { std::vector results; return results; } /******************************************************************* * Settings API ******************************************************************/ SoapySDR::ArgInfoList SoapyHydraSDR::getSettingInfo(void) const { SoapySDR::ArgInfoList setArgs; // Bias-T SoapySDR::ArgInfo biasOffsetArg; biasOffsetArg.key = "biastee"; biasOffsetArg.value = "false"; biasOffsetArg.name = "Bias tee"; biasOffsetArg.description = "Enable the 4.5v DC Bias tee to power a LNA via antenna connection."; biasOffsetArg.type = SoapySDR::ArgInfo::BOOL; setArgs.push_back(biasOffsetArg); // bitpack SoapySDR::ArgInfo bitPackingArg; bitPackingArg.key = "bitpack"; bitPackingArg.value = "false"; bitPackingArg.name = "Bit Pack"; bitPackingArg.description = "Enable packing 4 12-bit samples into 3 16-bit words for 25% less USB trafic."; bitPackingArg.type = SoapySDR::ArgInfo::BOOL; setArgs.push_back(bitPackingArg); return setArgs; } void SoapyHydraSDR::writeSetting(const std::string &key, const std::string &value) { if (key == "biastee") { bool enable = (value == "true"); rfBias = enable; hydrasdr_set_rf_bias(dev, enable); } if (key == "bitpack") { bool enable = (value == "true"); bitPack = enable; hydrasdr_set_packing(dev, enable); } } std::string SoapyHydraSDR::readSetting(const std::string &key) const { if (key == "biastee") { return rfBias?"true":"false"; } if (key == "bitpack") { return bitPack?"true":"false"; } // SoapySDR_logf(SOAPY_SDR_WARNING, "Unknown setting '%s'", key.c_str()); return ""; } hydrasdr-SoapyHydraSDR-253ce50/SoapyHydraSDR.hpp000066400000000000000000000204071512157426400214730ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_BUFFER_BYTES 262144 #define DEFAULT_NUM_BUFFERS 8 #define MAX_DEVICES 32 class SoapyHydraSDR: public SoapySDR::Device { public: SoapyHydraSDR(const SoapySDR::Kwargs &args); ~SoapyHydraSDR(void); /******************************************************************* * Identification API ******************************************************************/ std::string getDriverKey(void) const; std::string getHardwareKey(void) const; SoapySDR::Kwargs getHardwareInfo(void) const; /******************************************************************* * Channels API ******************************************************************/ size_t getNumChannels(const int) const; /******************************************************************* * Stream API ******************************************************************/ std::vector getStreamFormats(const int direction, const size_t channel) const; std::string getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const; SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, const size_t channel) const; SoapySDR::Stream *setupStream(const int direction, const std::string &format, const std::vector &channels = std::vector(), const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); void closeStream(SoapySDR::Stream *stream); size_t getStreamMTU(SoapySDR::Stream *stream) const; int activateStream( SoapySDR::Stream *stream, const int flags = 0, const long long timeNs = 0, const size_t numElems = 0); int deactivateStream(SoapySDR::Stream *stream, const int flags = 0, const long long timeNs = 0); int readStream( SoapySDR::Stream *stream, void * const *buffs, const size_t numElems, int &flags, long long &timeNs, const long timeoutUs = 100000); /******************************************************************* * Direct buffer access API ******************************************************************/ size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs); int acquireReadBuffer( SoapySDR::Stream *stream, size_t &handle, const void **buffs, int &flags, long long &timeNs, const long timeoutUs = 100000); void releaseReadBuffer( SoapySDR::Stream *stream, const size_t handle); /******************************************************************* * Antenna API ******************************************************************/ std::vector listAntennas(const int direction, const size_t channel) const; void setAntenna(const int direction, const size_t channel, const std::string &name); std::string getAntenna(const int direction, const size_t channel) const; /******************************************************************* * Frontend corrections API ******************************************************************/ bool hasDCOffsetMode(const int direction, const size_t channel) const; /******************************************************************* * Gain API ******************************************************************/ std::vector listGains(const int direction, const size_t channel) const; bool hasGainMode(const int direction, const size_t channel) const; void setGainMode(const int direction, const size_t channel, const bool automatic); bool getGainMode(const int direction, const size_t channel) const; void setGain(const int direction, const size_t channel, const double value); void setGain(const int direction, const size_t channel, const std::string &name, const double value); double getGain(const int direction, const size_t channel, const std::string &name) const; SoapySDR::Range getGainRange(const int direction, const size_t channel, const std::string &name) const; /******************************************************************* * Frequency API ******************************************************************/ void setFrequency( const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); double getFrequency(const int direction, const size_t channel, const std::string &name) const; std::vector listFrequencies(const int direction, const size_t channel) const; SoapySDR::RangeList getFrequencyRange(const int direction, const size_t channel, const std::string &name) const; SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, const size_t channel) const; /******************************************************************* * Sample Rate API ******************************************************************/ void setSampleRate(const int direction, const size_t channel, const double rate); double getSampleRate(const int direction, const size_t channel) const; std::vector listSampleRates(const int direction, const size_t channel) const; void setBandwidth(const int direction, const size_t channel, const double bw); double getBandwidth(const int direction, const size_t channel) const; std::vector listBandwidths(const int direction, const size_t channel) const; /******************************************************************* * Utility ******************************************************************/ /******************************************************************* * Settings API ******************************************************************/ SoapySDR::ArgInfoList getSettingInfo(void) const; void writeSetting(const std::string &key, const std::string &value); std::string readSetting(const std::string &key) const; private: //device handle uint64_t serial; struct hydrasdr_device *dev; //cached settings uint32_t sampleRate, centerFrequency; unsigned int bufferLength; size_t numBuffers; bool agcMode, streamActive, rfBias, bitPack; std::atomic_bool sampleRateChanged; int bytesPerSample; uint8_t lnaGain, mixerGain, vgaGain; public: //async api usage int rx_callback(hydrasdr_transfer *t); std::mutex _buf_mutex; std::condition_variable _buf_cond; std::vector > _buffs; size_t _buf_head; size_t _buf_tail; std::atomic _buf_count; char *_currentBuff; std::atomic _overflowEvent; size_t bufferedElems; size_t _currentHandle; bool resetBuffer; }; hydrasdr-SoapyHydraSDR-253ce50/Streaming.cpp000066400000000000000000000234011512157426400207600ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "SoapyHydraSDR.hpp" #include #include #include //min #include //SHRT_MAX #include // memcpy std::vector SoapyHydraSDR::getStreamFormats(const int direction, const size_t channel) const { std::vector formats; // formats.push_back("CS8"); formats.push_back(SOAPY_SDR_CS16); formats.push_back(SOAPY_SDR_CF32); return formats; } std::string SoapyHydraSDR::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const { fullScale = 32767; return SOAPY_SDR_CS16; } SoapySDR::ArgInfoList SoapyHydraSDR::getStreamArgsInfo(const int direction, const size_t channel) const { SoapySDR::ArgInfoList streamArgs; // SoapySDR::ArgInfo chanArg; // chanArg.key = "chan"; // chanArg.value = "mono_l"; // chanArg.name = "Channel Setup"; // chanArg.description = "Input channel configuration."; // chanArg.type = SoapySDR::ArgInfo::STRING; // std::vector chanOpts; // std::vector chanOptNames; // chanOpts.push_back("mono_l"); // chanOptNames.push_back("Mono Left"); // chanOpts.push_back("mono_r"); // chanOptNames.push_back("Mono Right"); // chanOpts.push_back("stereo_iq"); // chanOptNames.push_back("Complex L/R = I/Q"); // chanOpts.push_back("stereo_qi"); // chanOptNames.push_back("Complex L/R = Q/I"); // chanArg.options = chanOpts; // chanArg.optionNames = chanOptNames; // streamArgs.push_back(chanArg); return streamArgs; } /******************************************************************* * Async thread work ******************************************************************/ static int _rx_callback(hydrasdr_transfer *t) { //printf("_rx_callback\n"); SoapyHydraSDR *self = (SoapyHydraSDR *)t->ctx; return self->rx_callback(t); } int SoapyHydraSDR::rx_callback(hydrasdr_transfer *t) { if (sampleRateChanged.load()) { return 1; } //printf("_rx_callback %d _buf_head=%d, numBuffers=%d\n", len, _buf_head, _buf_tail); //overflow condition: the caller is not reading fast enough if (_buf_count == numBuffers) { _overflowEvent = true; return 0; } //copy into the buffer queue auto &buff = _buffs[_buf_tail]; buff.resize(t->sample_count * bytesPerSample); std::memcpy(buff.data(), t->samples, t->sample_count * bytesPerSample); //increment the tail pointer _buf_tail = (_buf_tail + 1) % numBuffers; //increment buffers available under lock //to avoid race in acquireReadBuffer wait { std::lock_guard lock(_buf_mutex); _buf_count++; } //notify readStream() _buf_cond.notify_one(); return 0; } /******************************************************************* * Stream API ******************************************************************/ SoapySDR::Stream *SoapyHydraSDR::setupStream( const int direction, const std::string &format, const std::vector &channels, const SoapySDR::Kwargs &args) { //check the channel configuration if (channels.size() > 1 or (channels.size() > 0 and channels.at(0) != 0)) { throw std::runtime_error("setupStream invalid channel selection"); } hydrasdr_sample_type asFormat = HYDRASDR_SAMPLE_INT16_IQ; //check the format if (format == SOAPY_SDR_CF32) { SoapySDR_log(SOAPY_SDR_INFO, "Using format CF32."); asFormat = HYDRASDR_SAMPLE_FLOAT32_IQ; } else if (format == SOAPY_SDR_CS16) { SoapySDR_log(SOAPY_SDR_INFO, "Using format CS16."); asFormat = HYDRASDR_SAMPLE_INT16_IQ; } else { throw std::runtime_error( "setupStream invalid format '" + format + "' -- Only CS16 and CF32 are supported by SoapyHydraSDR module."); } hydrasdr_set_sample_type(dev, asFormat); sampleRateChanged.store(true); bytesPerSample = SoapySDR::formatToSize(format); //We get this many complex samples over the bus. //Its the same for both complex float and int16. //TODO adjust when packing is enabled bufferLength = DEFAULT_BUFFER_BYTES/4; //clear async fifo counts _buf_tail = 0; _buf_count = 0; _buf_head = 0; //allocate buffers _buffs.resize(numBuffers); for (auto &buff : _buffs) buff.reserve(bufferLength*bytesPerSample); for (auto &buff : _buffs) buff.resize(bufferLength*bytesPerSample); return (SoapySDR::Stream *) this; } void SoapyHydraSDR::closeStream(SoapySDR::Stream *stream) { _buffs.clear(); } size_t SoapyHydraSDR::getStreamMTU(SoapySDR::Stream *stream) const { return bufferLength; } int SoapyHydraSDR::activateStream( SoapySDR::Stream *stream, const int flags, const long long timeNs, const size_t numElems) { if (flags != 0) { return SOAPY_SDR_NOT_SUPPORTED; } resetBuffer = true; bufferedElems = 0; if (sampleRateChanged.load()) { hydrasdr_set_samplerate(dev, sampleRate); sampleRateChanged.store(false); } hydrasdr_start_rx(dev, &_rx_callback, (void *) this); return 0; } int SoapyHydraSDR::deactivateStream(SoapySDR::Stream *stream, const int flags, const long long timeNs) { if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED; hydrasdr_stop_rx(dev); streamActive = false; return 0; } int SoapyHydraSDR::readStream( SoapySDR::Stream *stream, void * const *buffs, const size_t numElems, int &flags, long long &timeNs, const long timeoutUs) { if (!hydrasdr_is_streaming(dev)) { return 0; } if (sampleRateChanged.load()) { hydrasdr_stop_rx(dev); hydrasdr_set_samplerate(dev, sampleRate); hydrasdr_start_rx(dev, &_rx_callback, (void *) this); sampleRateChanged.store(false); } //this is the user's buffer for channel 0 void *buff0 = buffs[0]; //are elements left in the buffer? if not, do a new read. if (bufferedElems == 0) { int ret = this->acquireReadBuffer(stream, _currentHandle, (const void **)&_currentBuff, flags, timeNs, timeoutUs); if (ret < 0) return ret; bufferedElems = ret; } size_t returnedElems = std::min(bufferedElems, numElems); //convert into user's buff0 std::memcpy(buff0, _currentBuff, returnedElems * bytesPerSample); //bump variables for next call into readStream bufferedElems -= returnedElems; _currentBuff += returnedElems * bytesPerSample; //return number of elements written to buff0 if (bufferedElems != 0) flags |= SOAPY_SDR_MORE_FRAGMENTS; else this->releaseReadBuffer(stream, _currentHandle); return returnedElems; } /******************************************************************* * Direct buffer access API ******************************************************************/ size_t SoapyHydraSDR::getNumDirectAccessBuffers(SoapySDR::Stream *stream) { return _buffs.size(); } int SoapyHydraSDR::getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs) { buffs[0] = (void *)_buffs[handle].data(); return 0; } int SoapyHydraSDR::acquireReadBuffer( SoapySDR::Stream *stream, size_t &handle, const void **buffs, int &flags, long long &timeNs, const long timeoutUs) { //reset is issued by various settings //to drain old data out of the queue if (resetBuffer) { //drain all buffers from the fifo _buf_head = (_buf_head + _buf_count.exchange(0)) % numBuffers; resetBuffer = false; _overflowEvent = false; } //handle overflow from the rx callback thread if (_overflowEvent) { //drain the old buffers from the fifo _buf_head = (_buf_head + _buf_count.exchange(0)) % numBuffers; _overflowEvent = false; SoapySDR::log(SOAPY_SDR_SSI, "O"); return SOAPY_SDR_OVERFLOW; } //wait for a buffer to become available if (_buf_count == 0) { std::unique_lock lock(_buf_mutex); _buf_cond.wait_for(lock, std::chrono::microseconds(timeoutUs), [this]{return _buf_count != 0;}); if (_buf_count == 0) return SOAPY_SDR_TIMEOUT; } //extract handle and buffer handle = _buf_head; _buf_head = (_buf_head + 1) % numBuffers; buffs[0] = (void *)_buffs[handle].data(); flags = 0; //return number available return _buffs[handle].size() / bytesPerSample; } void SoapyHydraSDR::releaseReadBuffer( SoapySDR::Stream *stream, const size_t handle) { //TODO this wont handle out of order releases _buf_count--; } hydrasdr-SoapyHydraSDR-253ce50/cmake/000077500000000000000000000000001512157426400174035ustar00rootroot00000000000000hydrasdr-SoapyHydraSDR-253ce50/cmake/FindLibHYDRASDR.cmake000066400000000000000000000076711512157426400230700ustar00rootroot00000000000000# - Try to find LibHYDRASDR # Once done this will define # # LibHYDRASDR_FOUND - System has libhydrasdr # LibHYDRASDR_INCLUDE_DIRS - The libhydrasdr include directories # LibHYDRASDR_LIBRARIES - The libraries needed to use libhydrasdr # LibHYDRASDR_CFLAGS_OTHER - Compiler switches required for using libhydrasdr # LibHYDRASDR_VERSION - The libhydrasdr version find_package(PkgConfig) pkg_check_modules(PC_LibHYDRASDR hydrasdr) # Try alternative pkg-config names if first attempt fails if(NOT PC_LibHYDRASDR_FOUND) pkg_check_modules(PC_LibHYDRASDR libhydrasdr) endif() set(LibHYDRASDR_CFLAGS_OTHER ${PC_LibHYDRASDR_CFLAGS_OTHER}) # Define possible project-specific installation paths set(PROJECT_INSTALL_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/rfone_host_build/install/include" "${CMAKE_CURRENT_SOURCE_DIR}/../rfone_host_build/install/include" "${CMAKE_CURRENT_BINARY_DIR}/rfone_host_build/install/include" "${CMAKE_CURRENT_BINARY_DIR}/../rfone_host_build/install/include" ) set(PROJECT_LIB_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/rfone_host_build/install/lib" "${CMAKE_CURRENT_SOURCE_DIR}/rfone_host_build/install/lib64" "${CMAKE_CURRENT_SOURCE_DIR}/../rfone_host_build/install/lib" "${CMAKE_CURRENT_SOURCE_DIR}/../rfone_host_build/install/lib64" "${CMAKE_CURRENT_BINARY_DIR}/rfone_host_build/install/lib" "${CMAKE_CURRENT_BINARY_DIR}/rfone_host_build/install/lib64" "${CMAKE_CURRENT_BINARY_DIR}/../rfone_host_build/install/lib" "${CMAKE_CURRENT_BINARY_DIR}/../rfone_host_build/install/lib64" ) # Windows-specific paths (vcpkg) if(WIN32) list(APPEND PROJECT_INSTALL_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/rfone_host_build/install/include" "${LIBHYDRASDR_ROOT}/include" ) list(APPEND PROJECT_LIB_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/rfone_host_build/install/bin" "${LIBHYDRASDR_ROOT}/lib" "${LIBHYDRASDR_ROOT}/bin" ) endif() find_path( LibHYDRASDR_INCLUDE_DIRS NAMES hydrasdr.h libhydrasdr/hydrasdr.h HINTS $ENV{LibHYDRASDR_DIR}/include ${PC_LibHYDRASDR_INCLUDEDIR} PATHS # Project-specific paths (for CI builds) ${PROJECT_INSTALL_PATHS} # Standard system paths /usr/local/include /usr/include /opt/local/include PATH_SUFFIXES libhydrasdr ) find_library( LibHYDRASDR_LIBRARIES NAMES hydrasdr libhydrasdr HINTS $ENV{LibHYDRASDR_DIR}/lib ${PC_LibHYDRASDR_LIBDIR} PATHS # Project-specific paths (for CI builds) ${PROJECT_LIB_PATHS} # Standard system paths /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib64 /opt/local/lib ) set(LibHYDRASDR_VERSION ${PC_LibHYDRASDR_VERSION}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LibHYDRASDR_FOUND to TRUE # if all listed variables are TRUE # Note that `FOUND_VAR LibHYDRASDR_FOUND` is needed for cmake 3.2 and older. find_package_handle_standard_args(LibHYDRASDR FOUND_VAR LibHYDRASDR_FOUND REQUIRED_VARS LibHYDRASDR_LIBRARIES LibHYDRASDR_INCLUDE_DIRS VERSION_VAR LibHYDRASDR_VERSION) mark_as_advanced(LibHYDRASDR_LIBRARIES LibHYDRASDR_INCLUDE_DIRS) # Debug output when found if(LibHYDRASDR_FOUND) message(STATUS "LibHydraSDR found:") message(STATUS " Version: ${LibHYDRASDR_VERSION}") message(STATUS " Libraries: ${LibHYDRASDR_LIBRARIES}") message(STATUS " Include dirs: ${LibHYDRASDR_INCLUDE_DIRS}") message(STATUS " CFlags: ${LibHYDRASDR_CFLAGS_OTHER}") else() message(STATUS "LibHydraSDR NOT found. Searched in:") message(STATUS " Project paths: ${PROJECT_INSTALL_PATHS}") message(STATUS " System paths: /usr/local/include, /usr/include") message(STATUS " Library paths: ${PROJECT_LIB_PATHS}") message(STATUS " System lib paths: /usr/local/lib, /usr/lib, /usr/lib64, /usr/lib/x86_64-linux-gnu") endif() hydrasdr-SoapyHydraSDR-253ce50/debian/000077500000000000000000000000001512157426400175455ustar00rootroot00000000000000hydrasdr-SoapyHydraSDR-253ce50/debian/changelog000066400000000000000000000004521512157426400214200ustar00rootroot00000000000000soapyhydrasdr (1.0.0-1) unstable; urgency=medium * New upstream master -- Daniel Ekman Sun, 07 Dec 2025 16:23:18 +0000 soapyhydrasdr (0.1.0-1) unstable; urgency=low * Release 0.1.0 (2025-06-14) -- PE0SAT Sat, 14 Jul 2025 10:42:53 +0200 hydrasdr-SoapyHydraSDR-253ce50/debian/compat000066400000000000000000000000031512157426400207440ustar00rootroot0000000000000011 hydrasdr-SoapyHydraSDR-253ce50/debian/control000066400000000000000000000015661512157426400211600ustar00rootroot00000000000000Source: soapyhydrasdr Section: hamradio Priority: optional Maintainer: Benjamin Vernoux Build-Depends: debhelper (>= 11.0.0), cmake, libsoapysdr-dev, libhydrasdr-dev Standards-Version: 4.5.0 Homepage: https://github.com/hydrasdr/SoapyHydraSDR Package: soapysdr0.7-module-hydrasdr Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: HydraSDR device support for SoapySDR The Soapy HydraSDR project provides a SoapySDR hardware support module. Package: soapysdr-module-hydrasdr Architecture: all Depends: soapysdr0.7-module-hydrasdr, ${misc:Depends} Description: HydraSDR device support for SoapySDR (default version) The Soapy HydraSDR project provides a SoapySDR hardware support module. . This is an empty dependency package that pulls in the HydraSDR module for the default version of libsoapysdr. hydrasdr-SoapyHydraSDR-253ce50/debian/copyright000066400000000000000000000023711512157426400215030ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: soapyhydrasdr Source: https://github.com/pothosware/SoapyHydraSDR/wiki Files: * Copyright: Copyright (c) 2016 Charles J. Cliffe License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hydrasdr-SoapyHydraSDR-253ce50/debian/docs000066400000000000000000000000121512157426400204110ustar00rootroot00000000000000README.md hydrasdr-SoapyHydraSDR-253ce50/debian/rules000077500000000000000000000005641512157426400206320ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --buildsystem=cmake --parallel override_dh_auto_configure: dh_auto_configure -- -DLIB_SUFFIX="/$(DEB_HOST_MULTIARCH)" override_dh_installchangelogs: dh_installchangelogs Changelog.txt hydrasdr-SoapyHydraSDR-253ce50/debian/soapysdr0.7-module-hydrasdr.install000066400000000000000000000000121512157426400263200ustar00rootroot00000000000000usr/lib/* hydrasdr-SoapyHydraSDR-253ce50/debian/source/000077500000000000000000000000001512157426400210455ustar00rootroot00000000000000hydrasdr-SoapyHydraSDR-253ce50/debian/source/format000066400000000000000000000000141512157426400222530ustar00rootroot000000000000003.0 (quilt)