diff --git a/examples/cpp_api/CMakeLists.txt b/examples/cpp_api/CMakeLists.txt new file mode 100644 index 0000000..4051055 --- /dev/null +++ b/examples/cpp_api/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.2) +project(test_app) + +# Find the package using pkg-config +find_package(PkgConfig REQUIRED) +pkg_check_modules(CARIBOULITE REQUIRED cariboulite) + +# Add the executable +add_executable(test_app main.cpp) + +# Include directories from the cariboulite package +target_include_directories(test_app PRIVATE ${CARIBOULITE_INCLUDE_DIRS}) + +# Link against the cariboulite library +target_link_libraries(test_app PRIVATE ${CARIBOULITE_LIBRARIES}) diff --git a/examples/cpp_api/main.cpp b/examples/cpp_api/main.cpp new file mode 100644 index 0000000..8104c0c --- /dev/null +++ b/examples/cpp_api/main.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +void printInfo(CaribouLite& cl) +{ + std::cout << "Initialized CaribouLite: " << cl.IsInitialized() << std::endl; + std::cout << "API Versions: " << cl.GetApiVersion() << std::endl; + std::cout << "Hardware Serial Number: " << std::hex << cl.GetHwSerialNumber() << std::endl; + std::cout << "System Type: " << cl.GetSystemVersionStr() << std::endl; + std::cout << "Hardware Unique ID: " << cl.GetHwGuid() << std::endl; +} + +void detectBoard() +{ + CaribouLite::SysVersion ver; + std::string name; + std::string guid; + + if (CaribouLite::DetectBoard(&ver, name, guid)) + { + std::cout << "Detected Version: " << CaribouLite::GetSystemVersionStr(ver) << ", Name: " << name << ", GUID: " << guid << std::endl; + } + else + { + std::cout << "Undetected CaribouLite!" << std::endl; + } +} + +void receivedSamples(CaribouLiteRadio* radio, const std::complex* samples, const bool* sync, size_t num_samples) +{ + std::cout << "Radio: " << radio->GetRadioName() << " Received " << num_samples << " samples" << std::endl; +} + +int main () +{ + detectBoard(); + CaribouLite &cl = CaribouLite::GetInstance(); + printInfo(cl); + + CaribouLiteRadio *s1g = cl.GetRadioChannel(CaribouLiteRadio::RadioType::S1G); + CaribouLiteRadio *hif = cl.GetRadioChannel(CaribouLiteRadio::RadioType::HiF); + + std::cout << "First Radio Name: " << s1g->GetRadioName() << " MtuSize: " << s1g->GetNativeMtuSample() << " Samples" << std::endl; + std::cout << "First Radio Name: " << hif->GetRadioName() << " MtuSize: " << hif->GetNativeMtuSample() << " Samples" << std::endl; + + s1g->SetFrequency(900000000); + s1g->StartReceiving(receivedSamples, 20000); + + getchar(); + + return 0; +} diff --git a/software/gr-cariboulite/CMakeLists.txt b/software/gr-cariboulite/CMakeLists.txt index 816128b..f20c76c 100644 --- a/software/gr-cariboulite/CMakeLists.txt +++ b/software/gr-cariboulite/CMakeLists.txt @@ -12,7 +12,7 @@ ######################################################################## # External dependencies ######################################################################## -find_package(CaribouLite 1.0) +find_package(cariboulite) ######################################################################## # Register component diff --git a/software/libcariboulite/CMakeLists.txt b/software/libcariboulite/CMakeLists.txt index 6e69eb0..d7e74a3 100644 --- a/software/libcariboulite/CMakeLists.txt +++ b/software/libcariboulite/CMakeLists.txt @@ -30,6 +30,8 @@ set(TARGET_LINK_LIBS datatypes m pthread) +set(SOURCES_CPP_LIB src/CaribouLiteCpp.cpp src/CaribouLiteRadioCpp.cpp) + # Add internal project dependencies add_subdirectory(src/datatypes EXCLUDE_FROM_ALL) add_subdirectory(src/caribou_programming EXCLUDE_FROM_ALL) @@ -44,14 +46,14 @@ add_subdirectory(src/zf_log EXCLUDE_FROM_ALL) add_subdirectory(src/iir EXCLUDE_FROM_ALL) # Create the library cariboulite -add_library(cariboulite STATIC ${SOURCES_LIB}) +add_library(cariboulite STATIC ${SOURCES_LIB} ${SOURCES_CPP_LIB}) target_link_libraries(cariboulite PRIVATE ${TARGET_LINK_LIBS}) -set_target_properties(cariboulite PROPERTIES PUBLIC_HEADER "src/cariboulite.h;src/cariboulite_radio.h") +set_target_properties(cariboulite PROPERTIES PUBLIC_HEADER "src/cariboulite.h;src/cariboulite_radio.h;src/CaribouLite.hpp") set_target_properties(cariboulite PROPERTIES OUTPUT_NAME cariboulite) -add_library(cariboulite_shared SHARED ${SOURCES_LIB}) +add_library(cariboulite_shared SHARED ${SOURCES_LIB} ${SOURCES_CPP_LIB}) target_link_libraries(cariboulite_shared PRIVATE ${TARGET_LINK_LIBS}) -set_target_properties(cariboulite_shared PROPERTIES PUBLIC_HEADER "src/cariboulite.h;src/cariboulite_radio.h") +set_target_properties(cariboulite_shared PROPERTIES PUBLIC_HEADER "src/cariboulite.h;src/cariboulite_radio.h;src/CaribouLite.hpp") set_property(TARGET cariboulite_shared PROPERTY POSITION_INDEPENDENT_CODE 1) set_target_properties(cariboulite_shared PROPERTIES OUTPUT_NAME cariboulite) diff --git a/software/libcariboulite/src/CaribouLite.hpp b/software/libcariboulite/src/CaribouLite.hpp new file mode 100644 index 0000000..6daa7c0 --- /dev/null +++ b/software/libcariboulite/src/CaribouLite.hpp @@ -0,0 +1,227 @@ +/** + * @file CaribouLite.hpp + * @author David Michaeli + * @date September 2023 + * @brief Main Init/Close API + * + * A high level CPP API for CaribouLite + */ + + +#ifndef __CARIBOULITE_HPP__ +#define __CARIBOULITE_HPP__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if __cplusplus <= 199711L + #error This file needs at least a C++11 compliant compiler, try using: + #error $ g++ -std=c++11 .. +#endif + +/** + * @brief CaribouLite API Version + */ +class CaribouLiteVersion +{ +public: + CaribouLiteVersion(int v, int sv, int rev) {_ver.major_version = v; _ver.minor_version = sv; _ver.revision = rev;} + virtual ~CaribouLiteVersion() {} + + friend std::ostream& operator<<(std::ostream& os, const CaribouLiteVersion& c) + { + os << c._ver.major_version << '.' << c._ver.minor_version << '/' << c._ver.revision; + return os; + } +private: + cariboulite_lib_version_st _ver; +}; + +/** + * @brief CaribouLite Frequency Range + */ +class CaribouLiteFreqRange +{ +public: + CaribouLiteFreqRange(float min_hz, float max_hz) : _fMin(min_hz), _fMax(max_hz) {} + virtual ~CaribouLiteFreqRange() {} + friend std::ostream& operator<<(std::ostream& os, const CaribouLiteFreqRange& r) + { + os << "[" << r._fMin << ',' << r._fMax << ']'; + return os; + } +private: + float _fMin, _fMax; +}; + + +/** + * @brief CaribouLite Radio Channel + */ +class CaribouLite; +class CaribouLiteRadio +{ +public: + enum RadioType + { + S1G = 0, + HiF = 1, + }; + + enum RadioDir + { + Rx = 0, + Tx = 1, + }; + +public: + CaribouLiteRadio(const cariboulite_radio_state_st* radio, RadioType type, const CaribouLite* parent = NULL); + virtual ~CaribouLiteRadio(); + + // Gain + void SetAgc(bool agc_on); + bool GetAgc(void); + float GetRxGainMin(void); + float GetRxGainMax(void); + float GetRxGainSteps(void); + void SetRxGain(float gain); + float GetRxGain(void); + + // Tx Power + float GetTxPowerMin(void); + float GetTxPowerMax(void); + float GetTxPowerStep(void); + void SetTxPower(float pwr_dBm); + float GetTxPower(void); + + // Rx Bandwidth + float GetRxBandwidthMin(void); + float GetRxBandwidthMax(void); + void SetRxBandwidth(float bw_hz); + float GetRxBandwidth(void); + + // Tx Bandwidth + float GetTxBandwidthMin(void); + float GetTxBandwidthMax(void); + void SetTxBandwidth(float bw_hz); + float GetTxBandwidth(void); + + // Rx Sample Rate + float GetRxSampleRateMin(void); + float GetRxSampleRateMax(void); + void SetRxSampleRate(float sr_hz); + float GetRxSampleRate(void); + + // Tx Sample Rate + float GetTxSampleRateMin(void); + float GetTxSampleRateMax(void); + void SetTxSampleRate(float sr_hz); + float GetTxSampleRate(void); + + // RSSI and Rx Power and others + float GetRssi(void); + float GetEnergyDet(void); + unsigned char GetTrueRandVal(void); + + // Frequency Control + void SetFrequency(float freq_hz); + float GetFrequency(void); + std::vector GetFrequencyRange(void); + float GetFrequencyResolution(void); + + // Activation + void StartReceiving(std::function*, const bool*, size_t)> on_data_ready, size_t samples_per_chunk); + void StopReceiving(void); + void StartTransmitting(std::function*, const bool*, size_t*)> on_data_request, size_t samples_per_chunk); + void StartTransmittingLo(void); + void StartTransmittingCw(void); + void StopTransmitting(void); + bool GetIsTransmittingLo(void); + bool GetIsTransmittingCw(void); + + // General + size_t GetNativeMtuSample(void); + std::string GetRadioName(void); + +private: + const cariboulite_radio_state_st* _radio; + const CaribouLite* _device; + const RadioType _type; + + bool _rx_thread_running; + bool _rx_is_active; + std::thread *_rx_thread; + std::function*, const bool*, size_t)> _on_data_ready; + size_t _rx_samples_per_chunk; + + bool _tx_thread_running; + bool _tx_is_active; + std::thread *_tx_thread; + std::function*, const bool*, size_t*)> _on_data_request; + size_t _tx_samples_per_chunk; + +private: + static void CaribouLiteRxThread(CaribouLiteRadio* radio); + static void CaribouLiteTxThread(CaribouLiteRadio* radio); +}; + +/** + * @brief CaribouLite Device + */ +class CaribouLite +{ +public: + enum LogLevel + { + Verbose, + Info, + None, + }; + + enum SysVersion + { + Unknown = 0, + CaribouLiteFull = 1, + CaribouLiteISM = 2, + }; + +protected: + CaribouLite(bool forceFpgaProg = false, LogLevel logLvl = LogLevel::None); + CaribouLite(const CaribouLite& o) = delete; + void operator=(const CaribouLite&) = delete; + +public: + ~CaribouLite(); + bool IsInitialized(void); + CaribouLiteVersion GetApiVersion(void); + unsigned int GetHwSerialNumber(void); + SysVersion GetSystemVersion(void); + std::string GetSystemVersionStr(void); + static std::string GetSystemVersionStr(SysVersion v); + std::string GetHwGuid(void); + CaribouLiteRadio* GetRadioChannel(CaribouLiteRadio::RadioType ch); + + // Ststic detection and factory + static CaribouLite &GetInstance(bool forceFpgaProg = false, LogLevel logLvl = LogLevel::None); + static bool DetectBoard(SysVersion *sysVer, std::string& name, std::string& guid); + +private: + std::vector _channels; + SysVersion _systemVersion; + std::string _productName; + std::string _productGuid; + + static std::shared_ptr _instance; + static std::mutex _instMutex; +}; + +#endif // __CARIBOULITE_HPP__ \ No newline at end of file diff --git a/software/libcariboulite/src/CaribouLiteCpp.cpp b/software/libcariboulite/src/CaribouLiteCpp.cpp new file mode 100644 index 0000000..91ee131 --- /dev/null +++ b/software/libcariboulite/src/CaribouLiteCpp.cpp @@ -0,0 +1,142 @@ +#include +#include "CaribouLite.hpp" + +std::shared_ptr CaribouLite::_instance = nullptr; +std::mutex CaribouLite::_instMutex; + +//================================================================== +bool CaribouLite::DetectBoard(SysVersion *sysVer, std::string& name, std::string& guid) +{ + cariboulite_version_en hw_ver; + char hw_name[64]; + char hw_guid[64]; + bool detected = cariboulite_detect_connected_board(&hw_ver, hw_name, hw_guid); + if (detected) + { + name = std::string(hw_name); + guid = std::string(hw_guid); + if (sysVer) *sysVer = (SysVersion)hw_ver; + } + return detected; +} + +//================================================================== +CaribouLite &CaribouLite::GetInstance(bool forceFpgaProg, LogLevel logLvl) +{ + SysVersion ver; + std::string name, guid; + if (!DetectBoard(&ver, name, guid)) + { + throw std::runtime_error("CaribouLite was not detected"); + } + + std::lock_guard lock(_instMutex); + if (_instance == nullptr) + { + try + { + _instance = std::shared_ptr(new CaribouLite(forceFpgaProg, logLvl)); + } + catch (std::exception& e) + { + throw e; + } + } + return *_instance; +} + +//================================================================== +CaribouLite::CaribouLite(bool forceFpgaProg, LogLevel logLvl) +{ + if (cariboulite_init(forceFpgaProg, (cariboulite_log_level_en)logLvl) != 0) + { + throw std::runtime_error("Driver initialization failed"); + } + + // get information + DetectBoard(&_systemVersion, _productName, _productGuid); + + // populate the radio devices + cariboulite_radio_state_st *radio_s1g = cariboulite_get_radio(cariboulite_channel_s1g); + CaribouLiteRadio* radio_s1g_int = new CaribouLiteRadio(radio_s1g, CaribouLiteRadio::RadioType::S1G, this); + _channels.push_back(radio_s1g_int); + + cariboulite_radio_state_st *radio_hif = cariboulite_get_radio(cariboulite_channel_hif); + CaribouLiteRadio* radio_hif_int = new CaribouLiteRadio(radio_hif, CaribouLiteRadio::RadioType::HiF, this); + _channels.push_back(radio_hif_int); +} + +//================================================================== +CaribouLite::~CaribouLite() +{ + //std::cout << "Deleting Cariboulite ~CaribouLite" << std::endl; + if (_instance != nullptr) + { + for (size_t i = 0; i < _instance->_channels.size(); i++) + { + delete _instance->_channels[i]; + } + cariboulite_close(); + + _instance.reset(); + _instance = nullptr; + } +} + +//================================================================== +bool CaribouLite::IsInitialized() +{ + return cariboulite_is_initialized(); +} + +//================================================================== +CaribouLiteVersion CaribouLite::GetApiVersion() +{ + cariboulite_lib_version_st v = {0}; + cariboulite_get_lib_version(&v); + return CaribouLiteVersion(v.major_version, v.minor_version, v.revision); +} + +//================================================================== +unsigned int CaribouLite::GetHwSerialNumber() +{ + return cariboulite_get_sn(); +} + +//================================================================== +CaribouLite::SysVersion CaribouLite::GetSystemVersion() +{ + return (CaribouLite::SysVersion)cariboulite_get_version(); +} + +//================================================================== +std::string CaribouLite::GetSystemVersionStr(CaribouLite::SysVersion v) +{ + switch(v) + { + case SysVersion::CaribouLiteFull: return std::string("CaribouLite6G"); break; + case SysVersion::CaribouLiteISM: return std::string("CaribouLiteISM"); break; + case SysVersion::Unknown: + default: return std::string("Unknown CaribouLite"); break; + } + return std::string(""); // unreachable.. hopefully +} + +//================================================================== +std::string CaribouLite::GetSystemVersionStr(void) +{ + return CaribouLite::GetSystemVersionStr(GetSystemVersion()); +} + +//================================================================== +std::string CaribouLite::GetHwGuid(void) +{ + return _productGuid; +} + +//================================================================== +CaribouLiteRadio* CaribouLite::GetRadioChannel(CaribouLiteRadio::RadioType ch) +{ + return _channels[(int)ch]; +} + diff --git a/software/libcariboulite/src/CaribouLiteRadioCpp.cpp b/software/libcariboulite/src/CaribouLiteRadioCpp.cpp new file mode 100644 index 0000000..1edff5d --- /dev/null +++ b/software/libcariboulite/src/CaribouLiteRadioCpp.cpp @@ -0,0 +1,489 @@ +#include "CaribouLite.hpp" + +//================================================================= +void CaribouLiteRadio::CaribouLiteRxThread(CaribouLiteRadio* radio) +{ + size_t mtu_size = radio->GetNativeMtuSample(); + cariboulite_sample_complex_int16* rx_buffer = new cariboulite_sample_complex_int16[mtu_size]; + cariboulite_sample_meta* rx_meta_buffer = new cariboulite_sample_meta[mtu_size]; + std::complex* rx_copmlex_data = new std::complex[mtu_size]; + bool* rx_meta_data = new bool[mtu_size]; + + while (radio->_rx_thread_running) + { + if (!radio->_rx_is_active) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + + int ret = cariboulite_radio_read_samples((cariboulite_radio_state_st*)radio->_radio, + rx_buffer, + rx_meta_buffer, + mtu_size); + if (ret < 0) + { + if (ret == -1) + { + printf("reader thread failed to read SMI!\n"); + } + ret = 0; + continue; + } + + // convert the buffer + for (int i = 0; i < ret; i ++) + { + rx_meta_data[i] = rx_meta_buffer[i].sync; + rx_copmlex_data[i].real(rx_buffer[i].i / 4096.0); + rx_copmlex_data[i].imag(rx_buffer[i].q / 4096.0); + } + + // notify application + if (radio->_on_data_ready) + { + try + { + radio->_on_data_ready(radio, rx_copmlex_data, rx_meta_data, ret); + } + catch (std::exception &e) + { + std::cout << "OnDataReady Exception: " << e.what() << std::endl; + } + } + } + + delete[]rx_buffer; + delete[]rx_meta_buffer; + delete[]rx_copmlex_data; + delete[]rx_meta_data; +} + +//================================================================== +void CaribouLiteRadio::CaribouLiteTxThread(CaribouLiteRadio* radio) +{ + while (radio->_tx_thread_running) + { + if (!radio->_tx_is_active) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + // TBD + } +} + +//================================================================== +CaribouLiteRadio::CaribouLiteRadio(const cariboulite_radio_state_st* radio, RadioType type, const CaribouLite* parent) + : _radio(radio), _device(parent), _type(type) +{ + _rx_thread_running = true; + _rx_thread = new std::thread(CaribouLiteRadio::CaribouLiteRxThread, this); + + _tx_thread_running = true; + _tx_thread = new std::thread(CaribouLiteRadio::CaribouLiteTxThread, this); +} + +//================================================================== +CaribouLiteRadio::~CaribouLiteRadio() +{ + //std::cout << "Destructor of Radio" << std::endl; + StopReceiving(); + StopTransmitting(); + + _rx_thread_running = false; + _rx_thread->join(); + if (_rx_thread) delete _rx_thread; + + _tx_thread_running = false; + _tx_thread->join(); + if (_tx_thread) delete _tx_thread; +} + +// Gain +//================================================================== +void CaribouLiteRadio::SetAgc(bool agc_on) +{ + int gain = 0; + bool temp = false; + cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, &temp, &gain); + cariboulite_radio_set_rx_gain_control((cariboulite_radio_state_st*)_radio, agc_on, gain); +} + +//================================================================== +bool CaribouLiteRadio::GetAgc() +{ + int gain = 0; + bool temp = false; + cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, &temp, &gain); + return temp; +} + +//================================================================== +float CaribouLiteRadio::GetRxGainMin() +{ + int rx_min_gain_value_db, rx_max_gain_value_db, rx_gain_value_resolution_db; + cariboulite_radio_get_rx_gain_limits((cariboulite_radio_state_st*)_radio, + &rx_min_gain_value_db, + &rx_max_gain_value_db, + &rx_gain_value_resolution_db); + + return rx_min_gain_value_db; +} + +//================================================================== +float CaribouLiteRadio::GetRxGainMax() +{ + int rx_min_gain_value_db, rx_max_gain_value_db, rx_gain_value_resolution_db; + cariboulite_radio_get_rx_gain_limits((cariboulite_radio_state_st*)_radio, + &rx_min_gain_value_db, + &rx_max_gain_value_db, + &rx_gain_value_resolution_db); + + return rx_max_gain_value_db; +} + +//================================================================== +float CaribouLiteRadio::GetRxGainSteps() +{ + int rx_min_gain_value_db, rx_max_gain_value_db, rx_gain_value_resolution_db; + cariboulite_radio_get_rx_gain_limits((cariboulite_radio_state_st*)_radio, + &rx_min_gain_value_db, + &rx_max_gain_value_db, + &rx_gain_value_resolution_db); + + return rx_gain_value_resolution_db; +} + +//================================================================== +void CaribouLiteRadio::SetRxGain(float gain) +{ + bool temp = false; + cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, &temp, NULL); + cariboulite_radio_set_rx_gain_control((cariboulite_radio_state_st*)_radio, temp, (int)gain); +} + +//================================================================== +float CaribouLiteRadio::GetRxGain() +{ + int igain = 0; + cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, NULL, &igain); + return (float)igain; +} + +// Tx Power + +//================================================================== +float CaribouLiteRadio::GetTxPowerMin() +{ + return -15.0f; +} + +//================================================================== +float CaribouLiteRadio::GetTxPowerMax() +{ + return 18.0f; +} + +//================================================================== +float CaribouLiteRadio::GetTxPowerStep() +{ + return 1.0f; +} + +//================================================================== +void CaribouLiteRadio::SetTxPower(float pwr_dBm) +{ + cariboulite_radio_set_tx_power((cariboulite_radio_state_st*)_radio, (int)pwr_dBm); +} + +//================================================================== +float CaribouLiteRadio::GetTxPower() +{ + int itx_pwr = 0; + cariboulite_radio_get_tx_power((cariboulite_radio_state_st*)_radio, &itx_pwr); + return (float)itx_pwr; +} + +// Rx Bandwidth + +//================================================================== +float CaribouLiteRadio::GetRxBandwidthMin() +{ + return 200000.0f; +} + +//================================================================== +float CaribouLiteRadio::GetRxBandwidthMax() +{ + return 2500000.0f; +} + +//================================================================== +void CaribouLiteRadio::SetRxBandwidth(float bw_hz) +{ + cariboulite_radio_set_rx_bandwidth_flt((cariboulite_radio_state_st*)_radio, bw_hz); +} + +//================================================================== +float CaribouLiteRadio::GetRxBandwidth() +{ + float bw = 0; + cariboulite_radio_get_rx_bandwidth_flt((cariboulite_radio_state_st*)_radio, &bw); + return bw; +} + +// Tx Bandwidth + +//================================================================== +float CaribouLiteRadio::GetTxBandwidthMin() +{ + return 80000.0f; +} + +//================================================================== +float CaribouLiteRadio::GetTxBandwidthMax() +{ + return 1000000.0f; +} + +//================================================================== +void CaribouLiteRadio::SetTxBandwidth(float bw_hz) +{ + cariboulite_radio_set_tx_bandwidth_flt((cariboulite_radio_state_st*)_radio, bw_hz); +} + +//================================================================== +float CaribouLiteRadio::GetTxBandwidth() +{ + float bw = 0; + cariboulite_radio_get_tx_bandwidth_flt((cariboulite_radio_state_st*)_radio, &bw); + return bw; +} + +// Rx Sample Rate + +//================================================================== +float CaribouLiteRadio::GetRxSampleRateMin() +{ + return 400000.0f; +} + +//================================================================== +float CaribouLiteRadio::GetRxSampleRateMax() +{ + return 4000000.0f; +} + +//================================================================== +void CaribouLiteRadio::SetRxSampleRate(float sr_hz) +{ + cariboulite_radio_set_rx_sample_rate_flt((cariboulite_radio_state_st*)_radio, sr_hz); +} + +//================================================================== +float CaribouLiteRadio::GetRxSampleRate() +{ + float sr = 0.0f; + cariboulite_radio_get_rx_sample_rate_flt((cariboulite_radio_state_st*)_radio, &sr); + return sr; +} + +// Tx Sample Rate + +//================================================================== +float CaribouLiteRadio::GetTxSampleRateMin() +{ + return 400000.0f; +} + +//================================================================== +float CaribouLiteRadio::GetTxSampleRateMax() +{ + return 4000000.0f; +} + +//================================================================== +void CaribouLiteRadio::SetTxSampleRate(float sr_hz) +{ + cariboulite_radio_set_tx_samp_cutoff_flt((cariboulite_radio_state_st*)_radio, sr_hz); +} + +//================================================================== +float CaribouLiteRadio::GetTxSampleRate() +{ + float sr = 0.0f; + cariboulite_radio_get_tx_samp_cutoff_flt((cariboulite_radio_state_st*)_radio, &sr); + return sr; +} + +// RSSI and Rx Power and others + +//================================================================== +float CaribouLiteRadio::GetRssi() +{ + float rssi = 0.0f; + cariboulite_radio_get_rssi((cariboulite_radio_state_st*)_radio, &rssi); + return rssi; +} + +//================================================================== +float CaribouLiteRadio::GetEnergyDet() +{ + float ed = 0.0f; + cariboulite_radio_get_energy_det((cariboulite_radio_state_st*)_radio, &ed); + return ed; +} + +//================================================================== +unsigned char CaribouLiteRadio::GetTrueRandVal() +{ + unsigned char val = 0; + cariboulite_radio_get_rand_val((cariboulite_radio_state_st*)_radio, &val); + return val; +} + + +// Frequency Control + +//================================================================== +void CaribouLiteRadio::SetFrequency(float freq_hz) +{ + if (!cariboulite_frequency_available((cariboulite_channel_en)_type, freq_hz)) + { + char msg[128] = {0}; + sprintf(msg, "Frequency out or range %.2f Hz on %s", freq_hz, GetRadioName().c_str()); + throw std::invalid_argument(msg); + } + double freq_dbl = freq_hz; + if (cariboulite_radio_set_frequency((cariboulite_radio_state_st*)_radio, + true, + &freq_dbl) != 0) + { + char msg[128] = {0}; + sprintf(msg, "Frequency setting on %s failed", GetRadioName().c_str()); + throw std::runtime_error(msg); + } +} + +//================================================================== +float CaribouLiteRadio::GetFrequency() +{ + double freq = 0; + cariboulite_radio_get_frequency((cariboulite_radio_state_st*)_radio, + &freq, NULL, NULL); + return freq; +} + +//================================================================== +std::vector CaribouLiteRadio::GetFrequencyRange() +{ + std::vector ranges; + float freq_mins[10]; + float freq_maxs[10]; + int num = 0; + cariboulite_get_frequency_limits((cariboulite_channel_en)_type, freq_mins, freq_maxs, &num); + for (int i = 0; i < num; i++) + { + ranges.push_back(CaribouLiteFreqRange(freq_mins[i], freq_maxs[i])); + } + return ranges; +} + +//================================================================== +float CaribouLiteRadio::GetFrequencyResolution() +{ + // TBD: todo calculate it according to the real hardware specs + return 1.0f; +} + +// Activation + +//================================================================== +void CaribouLiteRadio::StartReceiving(std::function*, const bool*, size_t)> on_data_ready, size_t samples_per_chunk) +{ + _on_data_ready = on_data_ready; + _rx_samples_per_chunk = samples_per_chunk; + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_rx, true); + _rx_is_active = true; +} + +//================================================================== +void CaribouLiteRadio::StopReceiving() +{ + _rx_is_active = false; + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_rx, false); +} + +//================================================================== +void CaribouLiteRadio::StartTransmitting(std::function*, const bool*, size_t*)> on_data_request, size_t samples_per_chunk) +{ + _rx_is_active = false; + _on_data_request = on_data_request; + _tx_samples_per_chunk = samples_per_chunk; + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_rx, false); + cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, false, false); + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, true); + _tx_is_active = true; +} + +//================================================================== +void CaribouLiteRadio::StartTransmittingLo() +{ + _rx_is_active = false; + _tx_is_active = false; + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, false); + cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, true, false); + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, true); +} + +//================================================================== +void CaribouLiteRadio::StartTransmittingCw() +{ + _rx_is_active = false; + _tx_is_active = false; + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, false); + cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, false, true); + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, true); +} + +//================================================================== +void CaribouLiteRadio::StopTransmitting() +{ + _tx_is_active = false; + cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, false, false); + cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, false); +} + +//================================================================== +bool CaribouLiteRadio::GetIsTransmittingLo() +{ + bool lo_out = false; + cariboulite_radio_get_cw_outputs((cariboulite_radio_state_st*)_radio, &lo_out, NULL); + return lo_out; +} + +//================================================================== +bool CaribouLiteRadio::GetIsTransmittingCw() +{ + bool cw_out = false; + cariboulite_radio_get_cw_outputs((cariboulite_radio_state_st*)_radio, NULL, &cw_out); + return cw_out; +} + +// General + +//================================================================== +size_t CaribouLiteRadio::GetNativeMtuSample() +{ + return cariboulite_radio_get_native_mtu_size_samples((cariboulite_radio_state_st*)_radio); +} + +//================================================================== +std::string CaribouLiteRadio::GetRadioName() +{ + char name[64]; + cariboulite_get_channel_name((cariboulite_channel_en)_type, name, sizeof(name)); + return std::string(name); +} + diff --git a/software/libcariboulite/src/caribou_smi/caribou_smi.c b/software/libcariboulite/src/caribou_smi/caribou_smi.c index 0e262b9..a24302d 100644 --- a/software/libcariboulite/src/caribou_smi/caribou_smi.c +++ b/software/libcariboulite/src/caribou_smi/caribou_smi.c @@ -651,7 +651,7 @@ int caribou_smi_read(caribou_smi_st* dev, caribou_smi_channel_en channel, } else if (ret == 0) { - printf("caribou_smi_read -> Timeout\n"); + ZF_LOGD("Reading timed-out"); break; } else diff --git a/software/libcariboulite/src/cariboulite_radio.c b/software/libcariboulite/src/cariboulite_radio.c index 1ef5f9d..27ec8ac 100644 --- a/software/libcariboulite/src/cariboulite_radio.c +++ b/software/libcariboulite/src/cariboulite_radio.c @@ -305,21 +305,45 @@ int cariboulite_radio_set_rx_samp_cutoff(cariboulite_radio_state_st* radio, return 0; } +//========================================================================= +static cariboulite_radio_sample_rate_en sample_rate_from_flt(float sample_rate_hz) +{ + cariboulite_radio_sample_rate_en sample_rate = cariboulite_radio_rx_sample_rate_4000khz; + if (sample_rate_hz >= sample_rate_middles[0]) sample_rate = cariboulite_radio_rx_sample_rate_4000khz; + else if (sample_rate_hz >= sample_rate_middles[1]) sample_rate = cariboulite_radio_rx_sample_rate_2000khz; + else if (sample_rate_hz >= sample_rate_middles[2]) sample_rate = cariboulite_radio_rx_sample_rate_1333khz; + else if (sample_rate_hz >= sample_rate_middles[3]) sample_rate = cariboulite_radio_rx_sample_rate_1000khz; + else if (sample_rate_hz >= sample_rate_middles[4]) sample_rate = cariboulite_radio_rx_sample_rate_800khz; + else if (sample_rate_hz >= sample_rate_middles[5]) sample_rate = cariboulite_radio_rx_sample_rate_666khz; + else if (sample_rate_hz >= sample_rate_middles[6]) sample_rate = cariboulite_radio_rx_sample_rate_500khz; + else sample_rate = cariboulite_radio_rx_sample_rate_400khz; + return sample_rate; +} + +//========================================================================= +static float sample_rate_to_flt(cariboulite_radio_sample_rate_en sample_rate) +{ + float sample_rate_hz = 4000000; + switch(sample_rate) + { + case cariboulite_radio_rx_sample_rate_4000khz: sample_rate_hz = 4000000; break; + case cariboulite_radio_rx_sample_rate_2000khz: sample_rate_hz = 2000000; break; + case cariboulite_radio_rx_sample_rate_1333khz: sample_rate_hz = 1333000; break; + case cariboulite_radio_rx_sample_rate_1000khz: sample_rate_hz = 1000000; break; + case cariboulite_radio_rx_sample_rate_800khz: sample_rate_hz = 800000; break; + case cariboulite_radio_rx_sample_rate_666khz: sample_rate_hz = 666000; break; + case cariboulite_radio_rx_sample_rate_500khz: sample_rate_hz = 500000; break; + case cariboulite_radio_rx_sample_rate_400khz: sample_rate_hz = 400000; break; + default: sample_rate_hz = 4000000; break; + } + return sample_rate_hz; +} + //========================================================================= int cariboulite_radio_set_rx_sample_rate_flt(cariboulite_radio_state_st* radio, float sample_rate_hz) { - cariboulite_radio_sample_rate_en rx_sample_rate = cariboulite_radio_rx_sample_rate_4000khz; + cariboulite_radio_sample_rate_en rx_sample_rate = sample_rate_from_flt(sample_rate_hz); cariboulite_radio_f_cut_en rx_cutoff = radio->rx_fcut; - - if (sample_rate_hz >= sample_rate_middles[0]) rx_sample_rate = cariboulite_radio_rx_sample_rate_4000khz; - else if (sample_rate_hz >= sample_rate_middles[1]) rx_sample_rate = cariboulite_radio_rx_sample_rate_2000khz; - else if (sample_rate_hz >= sample_rate_middles[2]) rx_sample_rate = cariboulite_radio_rx_sample_rate_1333khz; - else if (sample_rate_hz >= sample_rate_middles[3]) rx_sample_rate = cariboulite_radio_rx_sample_rate_1000khz; - else if (sample_rate_hz >= sample_rate_middles[4]) rx_sample_rate = cariboulite_radio_rx_sample_rate_800khz; - else if (sample_rate_hz >= sample_rate_middles[5]) rx_sample_rate = cariboulite_radio_rx_sample_rate_666khz; - else if (sample_rate_hz >= sample_rate_middles[6]) rx_sample_rate = cariboulite_radio_rx_sample_rate_500khz; - else rx_sample_rate = cariboulite_radio_rx_sample_rate_400khz; - return cariboulite_radio_set_rx_samp_cutoff(radio, rx_sample_rate, rx_cutoff); } @@ -342,19 +366,7 @@ int cariboulite_radio_get_rx_sample_rate_flt(cariboulite_radio_state_st* radio, if (sample_rate_hz == NULL) return 0; - *sample_rate_hz = 4000000; - switch(rx_sample_rate) - { - case cariboulite_radio_rx_sample_rate_4000khz: *sample_rate_hz = 4000000; break; - case cariboulite_radio_rx_sample_rate_2000khz: *sample_rate_hz = 2000000; break; - case cariboulite_radio_rx_sample_rate_1333khz: *sample_rate_hz = 1333000; break; - case cariboulite_radio_rx_sample_rate_1000khz: *sample_rate_hz = 1000000; break; - case cariboulite_radio_rx_sample_rate_800khz: *sample_rate_hz = 800000; break; - case cariboulite_radio_rx_sample_rate_666khz: *sample_rate_hz = 666000; break; - case cariboulite_radio_rx_sample_rate_500khz: *sample_rate_hz = 500000; break; - case cariboulite_radio_rx_sample_rate_400khz: *sample_rate_hz = 400000; break; - default: *sample_rate_hz = 4000000; break; - } + *sample_rate_hz = sample_rate_to_flt(rx_sample_rate); return 0; } @@ -455,6 +467,27 @@ int cariboulite_radio_set_tx_bandwidth(cariboulite_radio_state_st* radio, return 0; } +//========================================================================= +int cariboulite_radio_set_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float tx_bw) +{ + cariboulite_radio_tx_cut_off_en bw = cariboulite_radio_tx_cut_off_80khz; + + if (tx_bw <= tx_bandwidth_middles[0]) bw = cariboulite_radio_tx_cut_off_80khz; + else if (tx_bw <= tx_bandwidth_middles[1]) bw = cariboulite_radio_tx_cut_off_100khz; + else if (tx_bw <= tx_bandwidth_middles[2]) bw = cariboulite_radio_tx_cut_off_125khz; + else if (tx_bw <= tx_bandwidth_middles[3]) bw = cariboulite_radio_tx_cut_off_160khz; + else if (tx_bw <= tx_bandwidth_middles[4]) bw = cariboulite_radio_tx_cut_off_200khz; + else if (tx_bw <= tx_bandwidth_middles[5]) bw = cariboulite_radio_tx_cut_off_250khz; + else if (tx_bw <= tx_bandwidth_middles[6]) bw = cariboulite_radio_tx_cut_off_315khz; + else if (tx_bw <= tx_bandwidth_middles[7]) bw = cariboulite_radio_tx_cut_off_400khz; + else if (tx_bw <= tx_bandwidth_middles[8]) bw = cariboulite_radio_tx_cut_off_500khz; + else if (tx_bw <= tx_bandwidth_middles[9]) bw = cariboulite_radio_tx_cut_off_625khz; + else if (tx_bw <= tx_bandwidth_middles[10]) bw = cariboulite_radio_tx_cut_off_800khz; + else bw = cariboulite_radio_tx_cut_off_1000khz; + + return cariboulite_radio_set_tx_bandwidth(radio, bw); +} + //========================================================================= int cariboulite_radio_get_tx_bandwidth(cariboulite_radio_state_st* radio, cariboulite_radio_tx_cut_off_en *tx_bw) @@ -464,6 +497,33 @@ int cariboulite_radio_get_tx_bandwidth(cariboulite_radio_state_st* radio, return 0; } +//========================================================================= +int cariboulite_radio_get_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float *tx_bw) +{ + cariboulite_radio_tx_cut_off_en bw; + cariboulite_radio_get_tx_bandwidth(radio, &bw); + + if (tx_bw == NULL) return 0; + + switch(bw) + { + case cariboulite_radio_tx_cut_off_80khz: *tx_bw = 80e5; break; + case cariboulite_radio_tx_cut_off_100khz: *tx_bw = 100e5; break; + case cariboulite_radio_tx_cut_off_125khz: *tx_bw = 125e5; break; + case cariboulite_radio_tx_cut_off_160khz: *tx_bw = 160e5; break; + case cariboulite_radio_tx_cut_off_200khz: *tx_bw = 200e5; break; + case cariboulite_radio_tx_cut_off_250khz: *tx_bw = 250e5; break; + case cariboulite_radio_tx_cut_off_315khz: *tx_bw = 315e5; break; + case cariboulite_radio_tx_cut_off_400khz: *tx_bw = 400e5; break; + case cariboulite_radio_tx_cut_off_500khz: *tx_bw = 500e5; break; + case cariboulite_radio_tx_cut_off_625khz: *tx_bw = 625e5; break; + case cariboulite_radio_tx_cut_off_800khz: *tx_bw = 800e5; break; + case cariboulite_radio_tx_cut_off_1000khz: + default: *tx_bw = 1000e5; break; + } + return 0; +} + //========================================================================= int cariboulite_radio_set_tx_samp_cutoff(cariboulite_radio_state_st* radio, cariboulite_radio_sample_rate_en tx_sample_rate, @@ -505,6 +565,14 @@ int cariboulite_radio_set_tx_samp_cutoff(cariboulite_radio_state_st* radio, return 0; } +//========================================================================= +int cariboulite_radio_set_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float sample_rate_hz) +{ + cariboulite_radio_sample_rate_en tx_sample_rate = sample_rate_from_flt(sample_rate_hz); + cariboulite_radio_f_cut_en tx_cutoff = radio->tx_fcut; + return cariboulite_radio_set_tx_samp_cutoff(radio, tx_sample_rate, tx_cutoff); +} + //========================================================================= int cariboulite_radio_get_tx_samp_cutoff(cariboulite_radio_state_st* radio, cariboulite_radio_sample_rate_en *tx_sample_rate, @@ -534,6 +602,19 @@ int cariboulite_radio_get_tx_samp_cutoff(cariboulite_radio_state_st* radio, return 0; } +//========================================================================= +int cariboulite_radio_get_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float *sample_rate_hz) +{ + cariboulite_radio_sample_rate_en tx_sample_rate; + cariboulite_radio_get_tx_samp_cutoff(radio, &tx_sample_rate, NULL); + + if (sample_rate_hz == NULL) return 0; + + *sample_rate_hz = sample_rate_to_flt(tx_sample_rate); + return 0; +} + + //========================================================================= int cariboulite_radio_get_rssi(cariboulite_radio_state_st* radio, float *rssi_dbm) { diff --git a/software/libcariboulite/src/cariboulite_radio.h b/software/libcariboulite/src/cariboulite_radio.h index f328688..696d440 100644 --- a/software/libcariboulite/src/cariboulite_radio.h +++ b/software/libcariboulite/src/cariboulite_radio.h @@ -406,6 +406,7 @@ int cariboulite_radio_get_tx_power(cariboulite_radio_state_st* radio, int cariboulite_radio_set_tx_bandwidth(cariboulite_radio_state_st* radio, cariboulite_radio_tx_cut_off_en tx_bw); +int cariboulite_radio_set_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float tx_bw); /** * @brief Modem get Tx analog Bandwidth @@ -418,6 +419,7 @@ int cariboulite_radio_set_tx_bandwidth(cariboulite_radio_state_st* radio, */ int cariboulite_radio_get_tx_bandwidth(cariboulite_radio_state_st* radio, cariboulite_radio_tx_cut_off_en *tx_bw); +int cariboulite_radio_get_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float *tx_bw); /** * @brief Modem set Tx digital bandwidth @@ -432,6 +434,7 @@ int cariboulite_radio_get_tx_bandwidth(cariboulite_radio_state_st* radio, int cariboulite_radio_set_tx_samp_cutoff(cariboulite_radio_state_st* radio, cariboulite_radio_sample_rate_en tx_sample_rate, cariboulite_radio_f_cut_en tx_cutoff); +int cariboulite_radio_set_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float sample_rate_hz); /** * @brief Modem get Tx digital bandwidth @@ -446,7 +449,7 @@ int cariboulite_radio_set_tx_samp_cutoff(cariboulite_radio_state_st* radio, int cariboulite_radio_get_tx_samp_cutoff(cariboulite_radio_state_st* radio, cariboulite_radio_sample_rate_en *tx_sample_rate, cariboulite_radio_f_cut_en *tx_cutoff); - +int cariboulite_radio_get_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float *sample_rate_hz); /** * @brief Modem get current RSSI @@ -571,16 +574,6 @@ int cariboulite_radio_set_cw_outputs(cariboulite_radio_state_st* radio, */ int cariboulite_radio_get_cw_outputs(cariboulite_radio_state_st* radio, bool *lo_out, bool *cw_out); -/** - * @brief Get the SMI native buffer length (samples) - * - * This function gets the internal MTU size (nominal transfer size) - * in samples. - * - * @param radio a pre-allocated radio state structure - * @return the native mtu size - */ -size_t cariboulite_radio_get_native_mtu_size_samples(cariboulite_radio_state_st* radio); /** * @brief Read samples