diff --git a/CMakeLists.txt b/CMakeLists.txt index 7319927..37a7b52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(m17cxx DESCRIPTION "M17 Digital Voice modulation and demodulation" LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) # Require out-of-source builds file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/CMakeLists.txt" LOC_PATH) @@ -24,10 +24,9 @@ message(STATUS "# Checking dependencies") set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) -include(FindPkgConfig) include(GNUInstallDirs) -pkg_check_modules(CODEC2 REQUIRED codec2) +find_package(codec2 REQUIRED) set(Boost_USE_STATIC_LIBS FALSE) find_package(Boost COMPONENTS program_options REQUIRED) diff --git a/README.md b/README.md index 426053b..7abb54b 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,20 @@ It also requires a modern C++17 compiler (GCC 8 minimum). make test sudo make install +## Build Steps for local building under Anaconda for Windows + +### Prequisites +- Microsoft Visual Studio 2019 +- Miniconda (or Anaconda) x64 for Windows + +### From a clean Conda environment + + conda config --add channels conda-forge + conda create -n M17 vs2019_win-64 cmake ninja pkg-config boost-cpp gtest gmock gtest libcodec2 + conda activate M17 + +### And then from the top level of the m17-cxx-demod repo, execute win_build.bat + ## Running This program was designed to be used with RTL-SDR, specifically rtl-fm. diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index a419e7c..ea00798 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(m17-demod m17-demod.cpp) -target_link_libraries(m17-demod PRIVATE m17cxx ${CODEC2_LIBRARIES} ${Boost_LIBRARIES}) +target_link_libraries(m17-demod PRIVATE m17cxx codec2 Boost::program_options) add_executable(m17-mod m17-mod.cpp) -target_link_libraries(m17-mod PRIVATE m17cxx ${CODEC2_LIBRARIES} ${Boost_LIBRARIES} Threads::Threads) +target_link_libraries(m17-mod PRIVATE m17cxx codec2 Boost::program_options Threads::Threads) install(TARGETS m17-demod m17-mod RUNTIME DESTINATION bin) diff --git a/apps/m17-demod.cpp b/apps/m17-demod.cpp index b26fad8..0dd7649 100644 --- a/apps/m17-demod.cpp +++ b/apps/m17-demod.cpp @@ -190,15 +190,15 @@ bool demodulate_audio(mobilinkd::M17FrameDecoder::audio_buffer_t const& audio, i if (noise_blanker && viterbi_cost > 80) { buf.fill(0); - std::cout.write((const char*)buf.begin(), 320); - std::cout.write((const char*)buf.begin(), 320); + std::cout.write((const char*)buf.data(), 320); + std::cout.write((const char*)buf.data(), 320); } else { - codec2_decode(codec2, buf.begin(), audio.begin() + 2); - std::cout.write((const char*)buf.begin(), 320); - codec2_decode(codec2, buf.begin(), audio.begin() + 10); - std::cout.write((const char*)buf.begin(), 320); + codec2_decode(codec2, buf.data(), audio.data() + 2); + std::cout.write((const char*)buf.data(), 320); + codec2_decode(codec2, buf.data(), audio.data() + 10); + std::cout.write((const char*)buf.data(), 320); } return result; diff --git a/include/m17cxx/ClockRecovery.h b/include/m17cxx/ClockRecovery.h index 611d2b4..37529e6 100644 --- a/include/m17cxx/ClockRecovery.h +++ b/include/m17cxx/ClockRecovery.h @@ -104,11 +104,11 @@ class ClockRecovery int8_t offset = sample_index_ - prev_sample_index_; // When in spec, the clock should drift by less than 1 sample per frame. - if (__builtin_expect(offset >= MAX_OFFSET, 0)) + if (offset >= MAX_OFFSET) [[unlikely]] { offset -= SAMPLES_PER_SYMBOL; } - else if (__builtin_expect(offset <= -MAX_OFFSET, 0)) + else if (offset <= -MAX_OFFSET) [[unlikely]] { offset += SAMPLES_PER_SYMBOL; } @@ -120,7 +120,7 @@ class ClockRecovery { // update_sample_index_() must be called first. - if (__builtin_expect((frame_count_ == 0), 0)) + if (frame_count_ == 0) [[unlikely]] { prev_sample_index_ = sample_index_; offset_ = 0.0; diff --git a/include/m17cxx/Convolution.h b/include/m17cxx/Convolution.h index e84eeac..122effa 100644 --- a/include/m17cxx/Convolution.h +++ b/include/m17cxx/Convolution.h @@ -2,20 +2,16 @@ #pragma once +#include #include #include -#ifdef _MSC_VER -# include -# define __builtin_popcount __popcnt -#endif - namespace mobilinkd { inline constexpr uint32_t convolve_bit(uint32_t poly, uint32_t memory) { - return __builtin_popcount(poly & memory) & 1; + return std::popcount(poly & memory) & 1; } template diff --git a/include/m17cxx/Golay24.h b/include/m17cxx/Golay24.h index d2d8512..20ee171 100644 --- a/include/m17cxx/Golay24.h +++ b/include/m17cxx/Golay24.h @@ -4,15 +4,11 @@ #pragma once #include +#include #include #include #include -#ifdef _MSC_VER -# include -# define __builtin_popcount __popcnt -#endif - namespace mobilinkd { // Parts are adapted from: @@ -90,11 +86,13 @@ constexpr array sort(array array) // static constexpr uint16_t POLY = 0xAE3; constexpr uint16_t POLY = 0xC75; -struct __attribute__((packed)) SyndromeMapEntry +#pragma pack(push, 1) +struct SyndromeMapEntry { uint32_t a{0}; uint16_t b{0}; }; +#pragma pack(pop) /** * Calculate the syndrome of a [23,12] Golay codeword. @@ -115,7 +113,7 @@ constexpr uint32_t syndrome(uint32_t codeword) constexpr bool parity(uint32_t codeword) { - return __builtin_popcount(codeword) & 1; + return std::popcount(codeword) & 1; } constexpr SyndromeMapEntry makeSyndromeMapEntry(uint64_t val) @@ -217,7 +215,7 @@ bool decode(uint32_t input, uint32_t& output) // Apply the correction to the input. output = input ^ correction; // Only test parity for 3-bit errors. - return __builtin_popcount(syndrm) < 3 || !parity(output); + return std::popcount(syndrm) < 3 || !parity(output); } return false; diff --git a/include/m17cxx/M17Demodulator.h b/include/m17cxx/M17Demodulator.h index 7970ed1..16b12a1 100644 --- a/include/m17cxx/M17Demodulator.h +++ b/include/m17cxx/M17Demodulator.h @@ -556,7 +556,7 @@ void M17Demodulator::operator()(const FloatType input) // We need to pump a few ms of data through on startup to initialize // the demodulator. - if (__builtin_expect((initializing), 0)) + if (initializing) [[unlikely]] { --initializing; initialize(input); diff --git a/include/m17cxx/M17Framer.h b/include/m17cxx/M17Framer.h index c87fa04..c80365e 100644 --- a/include/m17cxx/M17Framer.h +++ b/include/m17cxx/M17Framer.h @@ -32,7 +32,7 @@ struct M17Framer if (index_ == N) { index_ = 0; - *result = buffer_.begin(); + *result = buffer_.data(); return N; } return 0; @@ -46,7 +46,7 @@ struct M17Framer if (index_ == N) { index_ = 0; - *result = buffer_.begin(); + *result = buffer_.data(); return N; } return 0; diff --git a/include/m17cxx/M17Synchronizer.h b/include/m17cxx/M17Synchronizer.h index c7db653..422fd3a 100644 --- a/include/m17cxx/M17Synchronizer.h +++ b/include/m17cxx/M17Synchronizer.h @@ -2,13 +2,9 @@ #pragma once +#include #include -#ifdef _MSC_VER -# include -# define __builtin_popcount __popcnt -#endif - namespace mobilinkd { @@ -29,7 +25,7 @@ struct M17Synchronizer buffer_ = ((buffer_ << 2) | bits) & 0xFFFF; auto tmp = buffer_ ^ expected_; - return __builtin_popcount(tmp) <= allowable_errors_; + return std::popcount(tmp) <= allowable_errors_; } void reset() { buffer_ = 0; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c402b40..12a375d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,13 @@ target_include_directories(m17cxx INTERFACE $ ) -target_compile_features(m17cxx INTERFACE cxx_std_17) +target_compile_features(m17cxx INTERFACE cxx_std_20) + +if(MSVC) + # specify standards-conformance mode + target_compile_options(m17cxx INTERFACE /permissive-) + target_compile_definitions(m17cxx INTERFACE _USE_MATH_DEFINES) +endif() source_group( TREE "${PROJECT_SOURCE_DIR}/include" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d188b05..ce1ec95 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,71 +1,76 @@ include(GoogleTest) -pkg_check_modules(CODEC2 REQUIRED codec2) include_directories ( ${TEST_SOURCE_DIR} .. ) +if(WIN32) + set(PTHREAD "") +else() + set(PTHREAD "pthread") +endif(WIN32) + add_executable (ConvolutionTest ConvolutionTest.cpp) -target_link_libraries(ConvolutionTest m17cxx gtest pthread) +target_link_libraries(ConvolutionTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(ConvolutionTest "" AUTO) add_executable (M17FramerTest M17FramerTest.cpp) -target_link_libraries(M17FramerTest m17cxx gtest pthread) +target_link_libraries(M17FramerTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(M17FramerTest "" AUTO) add_executable (TrellisTest TrellisTest.cpp) -target_link_libraries(TrellisTest m17cxx gtest pthread) +target_link_libraries(TrellisTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(TrellisTest "" AUTO) add_executable (ViterbiTest ViterbiTest.cpp) -target_link_libraries(ViterbiTest m17cxx gtest pthread) +target_link_libraries(ViterbiTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(ViterbiTest "" AUTO) add_executable (Golay24Test Golay24Test.cpp) -target_link_libraries(Golay24Test m17cxx gtest pthread) +target_link_libraries(Golay24Test m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(Golay24Test "" AUTO) add_executable (CRC16Test CRC16Test.cpp) -target_link_libraries(CRC16Test m17cxx gtest pthread) +target_link_libraries(CRC16Test m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(CRC16Test "" AUTO) add_executable (M17RandomizerTest M17RandomizerTest.cpp) -target_link_libraries(M17RandomizerTest m17cxx gtest pthread) +target_link_libraries(M17RandomizerTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(M17RandomizerTest "" AUTO) add_executable (PolynomialInterleaverTest PolynomialInterleaverTest.cpp) -target_link_libraries(PolynomialInterleaverTest m17cxx gtest pthread) +target_link_libraries(PolynomialInterleaverTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(PolynomialInterleaverTest "" AUTO) add_executable (M17ModulatorTest M17ModulatorTest.cpp) -target_link_libraries(M17ModulatorTest m17cxx gtest pthread codec2) +target_link_libraries(M17ModulatorTest m17cxx GTest::GTest ${PTHREAD} codec2) gtest_add_tests(M17ModulatorTest "" AUTO) add_executable (UtilTest UtilTest.cpp) -target_link_libraries(UtilTest m17cxx gtest pthread) +target_link_libraries(UtilTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(UtilTest "" AUTO) add_executable (LinkSetupFrameTest LinkSetupFrameTest.cpp) -target_link_libraries(LinkSetupFrameTest m17cxx gtest pthread) +target_link_libraries(LinkSetupFrameTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(LinkSetupFrameTest "" AUTO) add_executable (SlidingDFTTest SlidingDFTTest.cpp) -target_link_libraries(SlidingDFTTest m17cxx gtest pthread) +target_link_libraries(SlidingDFTTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(SlidingDFTTest "" AUTO) add_executable (DataCarrierDetectTest DataCarrierDetectTest.cpp) -target_link_libraries(DataCarrierDetectTest m17cxx gtest pthread) +target_link_libraries(DataCarrierDetectTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(DataCarrierDetectTest "" AUTO) add_executable (ClockRecoveryTest ClockRecoveryTest.cpp) -target_link_libraries(ClockRecoveryTest m17cxx gtest pthread) +target_link_libraries(ClockRecoveryTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(ClockRecoveryTest "" AUTO) add_executable (FreqDevEstimatorTest FreqDevEstimatorTest.cpp) -target_link_libraries(FreqDevEstimatorTest m17cxx gtest pthread) +target_link_libraries(FreqDevEstimatorTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(FreqDevEstimatorTest "" AUTO) add_executable (CorrelatorTest CorrelatorTest.cpp) -target_link_libraries(CorrelatorTest m17cxx gtest pthread) +target_link_libraries(CorrelatorTest m17cxx GTest::GTest ${PTHREAD}) gtest_add_tests(CorrelatorTest "" AUTO) diff --git a/tests/UtilTest.cpp b/tests/UtilTest.cpp index 4adebe2..cfddcdf 100644 --- a/tests/UtilTest.cpp +++ b/tests/UtilTest.cpp @@ -2,6 +2,7 @@ #include +#include #include int main(int argc, char **argv) { @@ -210,7 +211,7 @@ TEST_F(UtilTest, PRBS9) uint16_t lfsr = 0x100; for (size_t i = 0; i != 511; ++i) { - lfsr = ((__builtin_popcount(lfsr & 0x11) & 1) << 8) | (lfsr >> 1); + lfsr = ((std::popcount(lfsr & 0x11u) & 1) << 8) | (lfsr >> 1); bool p = (lfsr & 0x100) == 0x100; bool n = prbs.generate(); EXPECT_EQ(p,n) << "i = " << i; diff --git a/win_build.bat b/win_build.bat new file mode 100644 index 0000000..3346e2b --- /dev/null +++ b/win_build.bat @@ -0,0 +1,36 @@ +:: +:: win_build.bat +:: +:: Batch file to locally build under Windows using Conda +:: +:: See README.md for details +:: +setlocal EnableDelayedExpansion +@echo on + +:: Set number of CPUs to use for build +set CPU_COUNT=3 + +:: Make a build folder and change to it +mkdir build +cd build + +:: configure +cmake -G "Ninja" ^ + -DCMAKE_BUILD_TYPE:STRING=Release ^ + -DCMAKE_INSTALL_PREFIX:PATH="%LIBRARY_PREFIX%" ^ + -DCMAKE_PREFIX_PATH:PATH="%LIBRARY_PREFIX%" ^ + .. +if errorlevel 1 exit /B 1 + +:: build +cmake --build . --config Release -- -j%CPU_COUNT% +if errorlevel 1 exit /B 1 + +:: install +cmake --build . --config Release --target install +if errorlevel 1 exit /B 1 + +:: test +ctest --build-config Release --output-on-failure --timeout 120 -j%CPU_COUNT% +if errorlevel 1 exit /B 1