diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 369b92d3..53794bea 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -29,7 +29,7 @@ jobs: sudo add-apt-repository ppa:gnuradio/gnuradio-releases sudo apt-get update sudo apt-get install -qq gnuradio-dev python3-packaging - sudo apt-get install -qq libusb-1.0-0-dev libwxbase3.0-dev libwxgtk3.0-gtk3-dev libsoapysdr-dev + sudo apt-get install -qq libusb-1.0-0-dev libwxbase3.0-dev libwxgtk3.0-gtk3-dev libsoapysdr-dev octave-dev - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{env.CMAKE_OPTIONS}} diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index ed377c63..c0a03247 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,7 +2,7 @@ ## Octave plugin integration ######################################################################## add_subdirectory(amarisoft-plugin) -# add_subdirectory(octave) +add_subdirectory(octave) ######################################################################## ## freedesktop environment integration diff --git a/plugins/octave/CMakeLists.txt b/plugins/octave/CMakeLists.txt index a3aa3a33..f83f7fa3 100644 --- a/plugins/octave/CMakeLists.txt +++ b/plugins/octave/CMakeLists.txt @@ -4,7 +4,7 @@ ######################################################################## find_program(OCTAVE_CONFIG_EXECUTABLE NAMES octave-config) if(NOT OCTAVE_CONFIG_EXECUTABLE) - message(STATUS "octave-config not found (need liboctave-dev), disabling LimeSuiteOctave") + message(STATUS "octave-config not found (need octave-dev), disabling LimeSuiteOctave") else() execute_process( COMMAND ${OCTAVE_CONFIG_EXECUTABLE} -p BINDIR @@ -33,9 +33,9 @@ endif() add_custom_command( OUTPUT LimeSuiteNG.oct LimeSuiteNG.o ALL COMMAND - ${OCTAVE_MKOCTFILE_EXECUTABLE} ARGS -I${PROJECT_SOURCE_DIR}/include/limesuiteng ${CMAKE_CURRENT_SOURCE_DIR}/LimeSuiteNG.cc - -L$ -llimesuiteng - COMMENT "Building LimeSuite Octave plugin" + ${OCTAVE_MKOCTFILE_EXECUTABLE} ARGS -std=c++17 -I${PROJECT_SOURCE_DIR}/src/include + ${CMAKE_CURRENT_SOURCE_DIR}/LimeSuiteNG.cc -L$ -llimesuiteng + COMMENT "Building LimeSuiteNG Octave plugin" DEPENDS limesuiteng) add_custom_target(LimeSuiteNGOctave ALL DEPENDS LimeSuiteNG.oct) @@ -44,7 +44,7 @@ execute_process( COMMAND ${OCTAVE_CONFIG_EXECUTABLE} --m-site-dir OUTPUT_VARIABLE OCTAVE_SCRIPTS_PATHS OUTPUT_STRIP_TRAILING_WHITESPACE) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LoadLimeSuite.m DESTINATION ${OCTAVE_SCRIPTS_PATHS}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/loader/LoadLimeSuiteNG.m DESTINATION ${OCTAVE_SCRIPTS_PATHS}) #install dynamic library execute_process( diff --git a/plugins/octave/LimeSuiteNG.cc b/plugins/octave/LimeSuiteNG.cc index 218796f9..4c012378 100644 --- a/plugins/octave/LimeSuiteNG.cc +++ b/plugins/octave/LimeSuiteNG.cc @@ -1,635 +1,576 @@ #include #include #include -#include + +#include "limesuiteng/complex.h" +#include "limesuiteng/DeviceHandle.h" +#include "limesuiteng/DeviceRegistry.h" +#include "limesuiteng/Logger.h" +#include "limesuiteng/SDRDescriptor.h" +#include "limesuiteng/SDRDevice.h" +#include "limesuiteng/StreamConfig.h" + +#include +#include #include +#include +#include +#include -#include "LimeSuite.h" +using namespace std::literals::string_literals; +using namespace std::literals::string_view_literals; -using namespace std; +constexpr uint8_t maxChCnt{ 2U }; +constexpr float scaleFactor{ 32768.0F }; -void FreeResources(); +lime::SDRDevice* lmsDev = nullptr; +bool IsWFMRunning = false; -const int maxChCnt = 2; -const float scaleFactor = 32768.0f; +lime::complex16_t** rxBuffers = nullptr; +uint8_t rxMaxChannel = 0; -lms_device_t* lmsDev = NULL; -lms_stream_t streamRx[maxChCnt]; -lms_stream_t streamTx[maxChCnt]; +lime::complex16_t** txBuffers = nullptr; +uint8_t txMaxChannel = 0; -struct complex16_t +void FreeResources() { - int16_t i; - int16_t q; -}; + if (lmsDev) + { + if (IsWFMRunning) + lmsDev->EnableTxWaveform(0, 0, false); + lmsDev->StreamStop(0); + lime::DeviceRegistry::freeDevice(lmsDev); + lmsDev = nullptr; + } -bool WFMrunning = false; -complex16_t* rxbuffers = NULL; -complex16_t* txbuffers = NULL; + if (rxBuffers != nullptr) + { + for (uint8_t i = 0; i <= rxMaxChannel; ++i) + { + delete[] rxBuffers[i]; + } + } -void StopStream() -{ - if(lmsDev == NULL) - return; - for (int i = 0; i < maxChCnt; i++) + delete[] rxBuffers; + rxBuffers = nullptr; + + if (txBuffers != nullptr) { - LMS_StopStream(&streamRx[i]); - LMS_StopStream(&streamTx[i]); - if (streamRx[i].handle) - LMS_DestroyStream(lmsDev,&streamRx[i]); - if (streamTx[i].handle) - LMS_DestroyStream(lmsDev,&streamTx[i]); - streamRx[i].handle = 0; - streamTx[i].handle = 0; + for (uint8_t i = 0; i <= txMaxChannel; ++i) + { + delete[] txBuffers[i]; + } } + + delete[] txBuffers; + txBuffers = nullptr; } -void PrintDeviceInfo(lms_device_t* port) +void PrintDeviceInfo() { - if(port == NULL || lmsDev == NULL) + if (lmsDev == nullptr) return; - const lms_dev_info_t* info = LMS_GetDeviceInfo(lmsDev); - if(info == NULL) - octave_stdout << "Failed to get device info" << endl; - else - { - octave_stdout << "Connected to device: " << info->deviceName - << " FW: " << info->firmwareVersion << " HW: " << info->hardwareVersion - << " Protocol: " << info->protocolVersion - << " GW: " << info->gatewareVersion << endl; - } -} + const auto& descriptor{ lmsDev->GetDescriptor() }; + octave_stdout << "Connected to device: " << descriptor.name << " FW: " << descriptor.firmwareVersion + << " HW: " << descriptor.hardwareVersion << " Protocol: " << descriptor.protocolVersion + << " GW: " << descriptor.gatewareVersion << std::endl; +} -static void LogHandler(int level, const char *msg) +static void LogHandler(lime::LogLevel level, const std::string& msg) { - const char* levelText[] = {"CRITICAL: " , "ERROR: ", "WARNING: ", "INFO: ", "DEBUG: "}; - if (level < 0) - level = 0; - else if (level >= 4) - return; //do not output debug messages - octave_stdout << levelText[level] << msg << endl; + const std::unordered_map levelText{ + { lime::LogLevel::Critical, "CRITICAL: "sv }, + { lime::LogLevel::Error, "ERROR: "sv }, + { lime::LogLevel::Warning, "WARNING: "sv }, + { lime::LogLevel::Info, "INFO: "sv }, + { lime::LogLevel::Debug, "DEBUG: "sv }, + { lime::LogLevel::Verbose, "VERBOSE: "sv }, + }; + if (level >= lime::LogLevel::Debug) + return; //do not output debug messages + octave_stdout << levelText.at(level) << msg << std::endl; } -DEFUN_DLD (LimeGetDeviceList, args, nargout, -"LIST = LimeGetDeviceList() - Returns available device list") +DEFUN_DLD(LimeGetDeviceList, args, nargout, "LIST = LimeGetDeviceList() - Returns available device list") { - if (args.length ()!=0) + if (!args.empty()) { print_usage(); return octave_value(-1); } - lms_info_str_t dev_list[64]; - int devCount = LMS_GetDeviceList((lms_info_str_t*)&dev_list); - dim_vector dim(devCount, 1); + + const auto& devices = lime::DeviceRegistry::enumerate(); Cell c; - octave_value_list retval; - //devNames.resize(devCount, 0, string("")); - for(int i=0; i1) + const int nargin = args.length(); + if (args.length() > 1) { print_usage(); return octave_value(-1); } - if(nargin > 0) + std::string deviceName = ""s; + if (nargin > 0) { - string deviceName = args(0).string_value(); - status = LMS_Open(&lmsDev, (char*)deviceName.c_str(), NULL); + deviceName = args(0).string_value(); } - else - status = LMS_Open(&lmsDev, NULL, NULL); - - LMS_RegisterLogHandler(LogHandler); - LMS_Synchronize(lmsDev,false); - if(status != 0) + lmsDev = lime::DeviceRegistry::makeDevice({ deviceName }); + if (lmsDev == nullptr) { - return octave_value(status); + return octave_value(-1); } - PrintDeviceInfo(lmsDev); - for (int i = 0; i < maxChCnt; i++) + lime::registerLogHandler(LogHandler); + + const auto status = lmsDev->Synchronize(false); + if (status != lime::OpStatus::Success) { - streamRx[i].handle = 0; - streamTx[i].handle = 0; + return octave_value(static_cast(status)); } + PrintDeviceInfo(); return octave_value(0); } - -DEFUN_DLD (LimeDestroy, args, nargout, -"LimeDestroy() - Stop all streams, deallocate memory and disconnect device") +DEFUN_DLD(LimeDestroy, args, nargout, "LimeDestroy() - Stop all streams, deallocate memory and disconnect device") { FreeResources(); return octave_value_list(); } -DEFUN_DLD (LimeLoadConfig, args, nargout, -"LimeLoadConfig(FILENAME) - Load configuration from file FILENAME") +DEFUN_DLD(LimeLoadConfig, args, nargout, "LimeLoadConfig(FILENAME) - Load configuration from file FILENAME") { - if(lmsDev == NULL) + if (lmsDev == nullptr) { - octave_stdout << "LimeSuite not initialized" << endl; + octave_stdout << "LimeSuite not initialized" << std::endl; return octave_value(-1); } - int nargin = args.length(); - - if(nargin != 1) + const int nargin = args.length(); + if (nargin != 1) { print_usage(); return octave_value(-1); } - string filename = args(0).string_value(); - octave_stdout << "LimeLoadConfig loading: " << filename << endl; - if (LMS_LoadConfig(lmsDev, filename.c_str())!=0) + const std::string filename = args(0).string_value(); + octave_stdout << "LimeLoadConfig loading: " << filename << std::endl; + + if (auto status = lmsDev->LoadConfig(0, filename); status != lime::OpStatus::Success) { - return octave_value(-1); + return octave_value(-1); } - int chCnt = LMS_GetNumChannels(lmsDev, LMS_CH_RX); - chCnt = chCnt > maxChCnt ? maxChCnt : chCnt; - for(int ch=0; ch< chCnt; ++ch) //set antenna to update RF switches + const uint8_t chCnt = std::min(lmsDev->GetDescriptor().rfSOC.at(0).channelCount, maxChCnt); + for (uint8_t ch = 0; ch < chCnt; ++ch) //set antenna to update RF switches { - int ant = LMS_GetAntenna(lmsDev, LMS_CH_RX, ch); - if(ant < 0 || LMS_SetAntenna(lmsDev, LMS_CH_RX, ch, ant) < 0) - octave_stdout << "Error setting Rx antenna for ch: " << ch << endl; - ant = LMS_GetAntenna(lmsDev, LMS_CH_TX, ch); - if(ant < 0 || LMS_SetAntenna(lmsDev, LMS_CH_TX, ch, ant) < 0) - octave_stdout << "Error setting Tx antenna for ch: " << ch << endl; + uint8_t ant = lmsDev->GetAntenna(0, lime::TRXDir::Rx, ch); + // int ant = LMS_GetAntenna(lmsDev, LMS_CH_RX, ch); + if (ant < 0 || lmsDev->SetAntenna(0, lime::TRXDir::Rx, ch, ant) != lime::OpStatus::Success) + octave_stdout << "Error setting Rx antenna for ch: " << ch << std::endl; + + ant = lmsDev->GetAntenna(0, lime::TRXDir::Tx, ch); + if (ant < 0 || lmsDev->SetAntenna(0, lime::TRXDir::Tx, ch, ant) != lime::OpStatus::Success) + octave_stdout << "Error setting Tx antenna for ch: " << ch << std::endl; } - octave_stdout << "Config loaded successfully: " << endl; + octave_stdout << "Config loaded successfully: " << filename << std::endl; return octave_value(0); } -DEFUN_DLD (LimeStartStreaming, args, nargout, -"LimeStartStreaming(FIFOSIZE, CHANNELS) - starts sample streaming from selected channels\n\ +lime::TRXDir GetDirection(std::string_view input) +{ + if (input == "rx"sv) + { + return lime::TRXDir::Rx; + } + if (input == "tx"sv) + { + return lime::TRXDir::Tx; + } + + throw std::runtime_error("Invalid direction"); +} + +uint8_t GetChannelNumber(const std::string& input) +{ + unsigned int ret = 0; + + if (!input.empty()) + { + std::stringstream ss{ input }; + ss >> ret; + } + + return ret; +} + +std::pair ParseDirectionAndChannel(const std::string& input) +{ + const std::string direction{ input.substr(0, 2) }; + const std::string channel{ input.substr(2) }; + + return { GetDirection(direction), GetChannelNumber(channel) }; +} + +DEFUN_DLD( + LimeStartStreaming, args, nargout, "LimeStartStreaming(FIFOSIZE, CHANNELS) - starts sample streaming from selected channels\n\ FIFOSIZE [optional] - buffer size in samples to be used by library (default: 4 MSamples)\ - CHANNELS [optional] - array of channels to be used [rx0 ; rx1 ; tx0 ; tx1] (deafult: rx0)") + CHANNELS [optional] - array of channels to be used [rx0 ; rx1 ; tx0 ; tx1] (default: rx0)") { - int nargin = args.length(); - if(lmsDev == NULL) + if (lmsDev == nullptr) { - octave_stdout << "LimeSuite not initialized" << endl; + octave_stdout << "LimeSuite not initialized" << std::endl; return octave_value(-1); } - bool tx[maxChCnt] = {false}; - bool rx[maxChCnt] = {false}; - int fifoSize = 4*1024*1024; //4MS - int channels = 1; - if ((nargin > 2)) + const int nargin = args.length(); + if (nargin > 2) { print_usage(); return octave_value(-1); } - if(nargin > 0 && args(0).int_value() > 0) + int fifoSize = 4 * 1024 * 1024; //4MS + if (nargin > 0 && args(0).int_value() > 0) fifoSize = args(0).int_value(); + lime::StreamConfig streamConfig{}; + streamConfig.format = lime::DataFormat::I16; + streamConfig.bufferSize = fifoSize; + if (nargin == 2) { - int rowCnt = args(1).char_matrix_value().rows();; - rowCnt = rowCnt < maxChCnt*2 ? rowCnt : maxChCnt*2; - for (int i = 0 ; i < rowCnt ; i++) + int rowCnt = args(1).char_matrix_value().rows(); + rowCnt = std::min(rowCnt, maxChCnt * 2); + for (int i = 0; i < rowCnt; ++i) { - std::string entry = args(1).char_matrix_value().row_as_string(i); - if (entry == "rx0" || entry == "rx") - rx[0] = true; - else if (entry == "rx1") - rx[1] = true; - else if (entry == "tx0" || entry == "tx") - tx[0] = true; - else if (entry == "tx1") - tx[1] = true; - else + const std::string entry = args(1).char_matrix_value().row_as_string(i); + + try { - octave_stdout << "Invalid channel parameter" << endl; + const auto [dir, ch] = ParseDirectionAndChannel(entry); + if (ch >= lmsDev->GetDescriptor().rfSOC.at(0).channelCount) + { + throw std::runtime_error("Invalid channel number"s); + } + streamConfig.channels[dir].push_back(ch); + } catch (...) + { + octave_stdout << "Invalid channel parameter" << std::endl; return octave_value(-1); } } } else - rx[0] = true; + { + streamConfig.channels[lime::TRXDir::Rx].push_back(0); + } - for (int i = 0; i < maxChCnt; i++) + if (!streamConfig.channels.at(lime::TRXDir::Rx).empty()) { - if (rx[i]) - { - streamRx[i].channel = i; - streamRx[i].fifoSize = fifoSize; - streamRx[i].dataFmt = lms_stream_t::LMS_FMT_I16; - streamRx[i].isTx = false; - streamRx[i].throughputVsLatency = 0.5; - LMS_SetupStream(lmsDev, &streamRx[i]); - } - if (tx[i]) + const auto& channels = streamConfig.channels.at(lime::TRXDir::Rx); + rxMaxChannel = *std::max_element(channels.begin(), channels.end()); + rxBuffers = new lime::complex16_t*[rxMaxChannel + 1]; + + for (uint8_t i = 0; i <= rxMaxChannel; ++i) { - streamTx[i].channel = i; - streamTx[i].fifoSize = fifoSize; - streamTx[i].dataFmt = lms_stream_t::LMS_FMT_I16; - streamTx[i].isTx = true; - streamTx[i].throughputVsLatency = 0.5; - LMS_SetupStream(lmsDev, &streamTx[i]); + rxBuffers[i] = new lime::complex16_t[fifoSize]; } } - for (int i = 0; i < maxChCnt; i++) + if (!streamConfig.channels.at(lime::TRXDir::Tx).empty()) { - if (rx[i]) - { - if (!rxbuffers) - rxbuffers = new complex16_t[fifoSize/2]; - LMS_StartStream(&streamRx[i]); - } - if (tx[i]) + const auto& channels = streamConfig.channels.at(lime::TRXDir::Tx); + txMaxChannel = *std::max_element(channels.begin(), channels.end()); + txBuffers = new lime::complex16_t*[txMaxChannel + 1]; + + for (uint8_t i = 0; i <= txMaxChannel; ++i) { - if (!txbuffers) - txbuffers = new complex16_t[fifoSize/2]; - LMS_StartStream(&streamTx[i]); + txBuffers[i] = new lime::complex16_t[fifoSize]; } } + lmsDev->StreamSetup(streamConfig, 0); + lmsDev->StreamStart(0); + return octave_value_list(); } -DEFUN_DLD (LimeStopStreaming, args, nargout, -"LimeStopStreaming() - Stop Receiver and Transmitter threads") +DEFUN_DLD(LimeStopStreaming, args, nargout, "LimeStopStreaming() - Stop Receiver and Transmitter threads") { - if(lmsDev == NULL) + if (lmsDev == nullptr) { - octave_stdout << "LimeSuite not initialized" << endl; + octave_stdout << "LimeSuite not initialized" << std::endl; return octave_value(-1); } - octave_stdout << "StopStreaming" << endl; - StopStream(); + octave_stdout << "StopStreaming" << std::endl; + lmsDev->StreamStop(0); return octave_value_list(); } -DEFUN_DLD (LimeReceiveSamples, args, , -"SIGNAL = LimeReceiveSamplesLarge( N, CH) - receive N samples from Rx channel CH.\n\ -CH parameter is optional, valid values are 0 and 1") +octave_value ReceiveSamples(int samplesToReceive, int offset = 0) { - if (!rxbuffers) - { - octave_stdout << "Rx streaming not initialized" << endl; - return octave_value_list();; - } + const Complex val{ 0.0F, 0.0F }; + ComplexMatrix iqData{ { rxMaxChannel + 1, samplesToReceive }, val }; // index 0 to N-1 - int nargin = args.length(); - if (nargin != 2 && nargin != 1) + int samplesCollected = 0; + int retries = 5; + while (samplesCollected < samplesToReceive && retries--) { - print_usage (); - return octave_value_list (); - } - const int samplesToReceive = args(0).int_value (); + const int samplesToRead = samplesToReceive - samplesCollected; - unsigned chIndex; - if (nargin == 2) - { - chIndex = args(1).int_value(); - if (chIndex >= maxChCnt) + const int samplesRead = lmsDev->StreamRx(0, rxBuffers, samplesToRead, nullptr); + if (samplesRead <= 0) { - octave_stdout << "Invalid channel number" << endl; - return octave_value_list();; + octave_stdout << "Error receiving samples" << std::endl; + return octave_value(-1); } - } - else - { - for (chIndex = 0; chIndex < maxChCnt; chIndex++) - if (streamRx[chIndex].handle != 0) - break; - } - Complex val=Complex(0.0,0.0); - ComplexRowVector iqdata( samplesToReceive, val ); // index 0 to N-1 + offset -= samplesRead; + offset = std::max(offset, 0); - const int timeout_ms = 1000; - lms_stream_meta_t meta; - int samplesCollected = 0; - int retries = 5; - while(samplesCollected < samplesToReceive && retries--) - { - int samplesToRead = 0; - if(samplesToReceive-samplesCollected > streamRx[chIndex].fifoSize/2) - samplesToRead = streamRx[chIndex].fifoSize/2; - else - samplesToRead = samplesToReceive-samplesCollected; - int samplesRead = LMS_RecvStream(&streamRx[chIndex], (void*)rxbuffers, samplesToRead, &meta, timeout_ms); - if (samplesRead < 0) - { - octave_stdout << "Error reading samples" << endl; - return octave_value(-1); - } - for(int i=0; i= maxChCnt) + for (int i = 0; i < samplesCount; ++i) { - octave_stdout << "Invalid channel number" << endl; - return octave_value(-1); + const octave_value iqDatum = scaleFactor * iqData(ch, i); + const Complex iqDatum2 = iqDatum.complex_value(); + const short i_sample = iqDatum2.real(); // + const short q_sample = iqDatum2.imag(); // + txBuffers[ch][i].real(i_sample); + txBuffers[ch][i].imag(q_sample); } } - else - { - for (chIndex = 0; chIndex < maxChCnt; chIndex++) - if (streamTx[chIndex].handle != 0) - break; - } - ComplexRowVector iqdata=args(0).complex_row_vector_value(); - dim_vector iqdataSize=iqdata.dims(); - int samplesCount = iqdataSize(0) > iqdataSize(1) ? iqdataSize(0) : iqdataSize(1); + const lime::StreamMeta meta{ 0, false, false }; + return lmsDev->StreamTx(0, txBuffers, samplesCount, &meta); +} - for(int i=0; i < samplesCount; ++i) +DEFUN_DLD(LimeTransmitSamples, args, , "LimeTransmitSamples(SIGNAL) - sends normalized complex SIGNAL to all active Tx channels.") +{ + if (!txBuffers) { - octave_value iqdatum = scaleFactor*iqdata(i); - Complex iqdatum2 = iqdatum.complex_value(); - short i_sample = iqdatum2.real(); // - short q_sample = iqdatum2.imag(); // - txbuffers[i].i = i_sample; - txbuffers[i].q = q_sample; + octave_stdout << "Tx streaming not initialized" << std::endl; + return octave_value(-1); } - const int timeout_ms = 1000; - lms_stream_meta_t meta; - meta.waitForTimestamp = false; - meta.timestamp = 0; - int samplesWrite = samplesCount; - samplesWrite = LMS_SendStream(&streamTx[chIndex], (const void*)txbuffers, samplesCount, &meta, timeout_ms); + const int nargin = args.length(); + if (nargin != 1) + { + print_usage(); + return octave_value(-1); + } - return octave_value (samplesWrite); + const ComplexMatrix iqData{ args(0).complex_matrix_value() }; + return octave_value(TransmitSamples(iqData)); } - -DEFUN_DLD (LimeTransceiveSamples, args, , -"RXSIGNAL = LimeTransceiveSamples( TXSIGNAL, RXOFFSET, CH) - transmit TXSIGNAL and receive RXSIGNAL (same length as TXSIGNAL).\n\ - RXOFFSET [optional] - number of samples to skip at the beginning of receive (default 0)\n\ - CH [optional] - channel to use for transmit and receive, valid values are 0 and 1 (default 0)") +DEFUN_DLD(LimeTransceiveSamples, + args, + , + "RXSIGNAL = LimeTransceiveSamples(TXSIGNAL, RXOFFSET) - transmit TXSIGNAL and receive RXSIGNAL (same length as TXSIGNAL).\n\ + RXOFFSET [optional] - number of samples to skip at the beginning of receive (default 0)") { - if (!rxbuffers) + if (!rxBuffers) { - octave_stdout << "Rx streaming not initialized" << endl; + octave_stdout << "Rx streaming not initialized" << std::endl; return octave_value_list(); } - if (!txbuffers) + if (!txBuffers) { - octave_stdout << "Tx streaming not initialized" << endl; + octave_stdout << "Tx streaming not initialized" << std::endl; return octave_value_list(); } - int nargin = args.length (); - if (nargin == 0 || nargin > 3) + const int nargin = args.length(); + if (nargin == 0 || nargin > 2) { - print_usage (); + print_usage(); return octave_value_list(); } - unsigned chIndex = 0; - if (nargin == 3) - { - chIndex = args(2).int_value (); - if ((chIndex >= maxChCnt) || (streamRx[chIndex].handle == 0) || (streamTx[chIndex].handle == 0)) - { - octave_stdout << "Invalid channel" << endl; - return octave_value_list(); - } - } - //transmit part - const int timeout_ms = 1000; - lms_stream_meta_t meta = {0, false, false}; - ComplexRowVector iqdataTx=args(0).complex_row_vector_value(); - dim_vector iqdataSize=iqdataTx.dims(); - const int samplesCount = iqdataSize(0) > iqdataSize(1) ? iqdataSize(0) : iqdataSize(1); - - for(int i=0; i < samplesCount; ++i) - { - octave_value iqdatum = scaleFactor*iqdataTx(i); - Complex iqdatum2 = iqdatum.complex_value(); - short i_sample = iqdatum2.real(); // - short q_sample = iqdatum2.imag(); // - txbuffers[i].i = i_sample; - txbuffers[i].q = q_sample; - } - const int samplesWrite = LMS_SendStream(&streamTx[chIndex], (const void*)txbuffers, samplesCount, &meta, timeout_ms); + const ComplexMatrix iqDataTx = args(0).complex_matrix_value(); + const dim_vector& iqDataSize = iqDataTx.dims(); + const int samplesCount = iqDataSize(1); + const int samplesWrite = TransmitSamples(iqDataTx); + if (samplesWrite != samplesCount) - octave_stdout << "Error transmitting samples: send " << samplesWrite +"/" +samplesCount << endl; + octave_stdout << "Error transmitting samples: send " << samplesWrite + "/" + samplesCount << std::endl; //Receive part int offset = nargin > 1 ? args(1).int_value() : 0; if (offset < 0) { - octave_stdout << "Invalid RXOFFSET value" << endl; + octave_stdout << "Invalid RXOFFSET value" << std::endl; offset = 0; } - Complex val=Complex(0.0,0.0); - ComplexRowVector iqdataRx( samplesCount, val ); // index 0 to N-1 - - int samplesCollected = 0; - int retries = 5; - while(samplesCollected < samplesCount && retries--) - { - int samplesToRead = 0; - if(samplesCount-samplesCollected > streamRx[chIndex].fifoSize/2) - samplesToRead = streamRx[chIndex].fifoSize/2; - else - samplesToRead = samplesCount-samplesCollected; - int samplesRead = LMS_RecvStream(&streamRx[chIndex], (void*)rxbuffers, samplesToRead, &meta, timeout_ms); - if (samplesRead < 0) - { - octave_stdout << "Error reading samples" << endl; - return octave_value(-1); - } - - if (offset >= samplesRead) - { - offset -= samplesRead; - continue; - } - for(int i=offset; i iqdataSize(1) ? iqdataSize(0) : iqdataSize(1); - int wfmLength = samplesCount; + ComplexRowVector iqData = args(0).complex_row_vector_value(); + dim_vector iqDataSize = iqData.dims(); + const int samplesCount = iqDataSize(0) > iqDataSize(1) ? iqDataSize(0) : iqDataSize(1); - complex16_t **wfmBuffers = new complex16_t*[chCount]; - for(int i=0; iUploadTxWaveform(streamConfig, 0, const_cast(reinterpret_cast(wfmBuffers)), samplesCount); + lmsDev->EnableTxWaveform(0, 0, true); + for (int i = 0; i < chCount; ++i) delete wfmBuffers[i]; - delete wfmBuffers; - WFMrunning = true; + delete[] wfmBuffers; + IsWFMRunning = true; return octave_value_list(); } -DEFUN_DLD (LimeLoopWFMStop, args, , -"LimeTxLoopWFMStop() - stop transmitting samples from device RAM") +DEFUN_DLD(LimeLoopWFMStop, args, , "LimeTxLoopWFMStop() - stop transmitting samples from device RAM") { - if(lmsDev == NULL) + if (lmsDev == nullptr) { - octave_stdout << "LimeSuite not initialized" << endl; + octave_stdout << "LimeSuite not initialized" << std::endl; return octave_value(-1); } - if(WFMrunning) - LMS_EnableTxWFM(lmsDev, 0, false); - WFMrunning = false; - return octave_value (); + if (IsWFMRunning) + lmsDev->EnableTxWaveform(0, 0, false); + IsWFMRunning = false; + return octave_value(); } -DEFUN_DLD (LimeGetStreamStatus, args, nargout, -"LimeGetStreamStatus() - Get Stream Status") +DEFUN_DLD(LimeGetStreamStatus, args, nargout, "LimeGetStreamStatus() - Get Stream Status") { - if(lmsDev == NULL) + if (lmsDev == nullptr) { - octave_stdout << "LimeSuite not initialized" << endl; + octave_stdout << "LimeSuite not initialized" << std::endl; return octave_value(-1); } octave_scalar_map st; - lms_stream_status_t status; - for (int i = 0; i < maxChCnt; i++) - { - if ((streamRx[i].handle) && (LMS_GetStreamStatus(&streamRx[i], &status) == 0)) - { - st.assign("fifo_size", status.fifoSize); - st.assign("rx_data_rate", status.linkRate); - std::string ch = std::string("rx") + char('0'+i); - st.assign(ch+"_fifo_filled", status.fifoFilledCount); - st.assign(ch+"_fifo_overruns", status.overrun); - st.assign(ch+"_lost_packets", status.droppedPackets); - } - if ((streamTx[i].handle)&& (LMS_GetStreamStatus(&streamTx[i], &status) == 0)) - { - st.assign("fifo_size", status.fifoSize); - st.assign("tx_data_rate", status.linkRate); - std::string ch = std::string("tx") + char('0'+i); - st.assign(ch+"_fifo_filled", status.fifoFilledCount); - st.assign(ch+"_fifo_underrun", status.underrun); - } - } - - return octave_value (st); -} -void FreeResources() -{ - if(lmsDev) - { - if(WFMrunning) - LMS_EnableTxWFM(lmsDev, 0, false); - StopStream(); - LMS_Close(lmsDev); - lmsDev = NULL; - } - if(rxbuffers) + if (rxBuffers != nullptr) { - delete rxbuffers; - rxbuffers = NULL; + lime::StreamStats status{}; + lmsDev->StreamStatus(0, &status, nullptr); + + st.assign("fifo_size", status.FIFO.totalCount); + st.assign("rx_data_rate", status.dataRate_Bps); + const std::string ch = "rx"s; + st.assign(ch + "_fifo_filled", status.FIFO.usedCount); + st.assign(ch + "_fifo_overruns", status.overrun); + st.assign(ch + "_lost_packets", status.loss); } - if(txbuffers) + + if (txBuffers != nullptr) { - delete txbuffers; - txbuffers = NULL; + lime::StreamStats status{}; + lmsDev->StreamStatus(0, nullptr, &status); + + st.assign("fifo_size", status.FIFO.totalCount); + st.assign("tx_data_rate", status.dataRate_Bps); + const std::string ch = "tx"s; + st.assign(ch + "_fifo_filled", status.FIFO.usedCount); + st.assign(ch + "_fifo_underrun", status.underrun); } + + return octave_value(st); } class ResourceDeallocator { -public: - ResourceDeallocator() {}; - ~ResourceDeallocator() - { - FreeResources(); - }; + public: + ResourceDeallocator() = default; + ~ResourceDeallocator() { FreeResources(); }; }; ResourceDeallocator gResources; diff --git a/plugins/octave/LoadLimeSuite.m b/plugins/octave/LoadLimeSuite.m deleted file mode 100644 index 8783c69f..00000000 --- a/plugins/octave/LoadLimeSuite.m +++ /dev/null @@ -1,13 +0,0 @@ -autoload('LimeInitialize', which('LimeSuite.oct')) -autoload('LimeGetDeviceInfo', which('LimeSuite.oct')) -autoload('LimeDestroy', which('LimeSuite.oct')) -autoload('LimeLoadConfig', which('LimeSuite.oct')) -autoload('LimeStartStreaming', which('LimeSuite.oct')) -autoload('LimeStopStreaming', which('LimeSuite.oct')) -autoload('LimeReceiveSamples', which('LimeSuite.oct')) -autoload('LimeTransmitSamples', which('LimeSuite.oct')) -autoload('LimeGetStreamStatus', which('LimeSuite.oct')) -autoload('LimeLoopWFMStart', which('LimeSuite.oct')) -autoload('LimeLoopWFMStop', which('LimeSuite.oct')) -autoload('LimeGetDeviceList', which('LimeSuite.oct')) -autoload('LimeTransceiveSamples', which('LimeSuite.oct')) \ No newline at end of file diff --git a/plugins/octave/.clang-format b/plugins/octave/examples/.clang-format similarity index 100% rename from plugins/octave/.clang-format rename to plugins/octave/examples/.clang-format diff --git a/plugins/octave/loop.ini b/plugins/octave/examples/loop.ini similarity index 100% rename from plugins/octave/loop.ini rename to plugins/octave/examples/loop.ini diff --git a/plugins/octave/loop_mini.ini b/plugins/octave/examples/loop_mini.ini similarity index 100% rename from plugins/octave/loop_mini.ini rename to plugins/octave/examples/loop_mini.ini diff --git a/plugins/octave/rx.m b/plugins/octave/examples/rx.m similarity index 89% rename from plugins/octave/rx.m rename to plugins/octave/examples/rx.m index a3abb737..c1b630bc 100644 --- a/plugins/octave/rx.m +++ b/plugins/octave/examples/rx.m @@ -1,4 +1,4 @@ -LoadLimeSuite +LoadLimeSuiteNG LimeInitialize() LimeLoadConfig('rxTest.ini'); @@ -25,6 +25,6 @@ LimeStopStreaming(); LimeDestroy(); -plot(real(samplesBatch1)); -%plot(real(samplesBatch2)); -%plot(real(samplesBatch3)); +plot(samplesBatch1); +%plot(samplesBatch2); +%plot(samplesBatch3); diff --git a/plugins/octave/rxTest.ini b/plugins/octave/examples/rxTest.ini similarity index 100% rename from plugins/octave/rxTest.ini rename to plugins/octave/examples/rxTest.ini diff --git a/plugins/octave/rx_2ch.m b/plugins/octave/examples/rx_2ch.m similarity index 74% rename from plugins/octave/rx_2ch.m rename to plugins/octave/examples/rx_2ch.m index 15fe3a60..d3ee713f 100644 --- a/plugins/octave/rx_2ch.m +++ b/plugins/octave/examples/rx_2ch.m @@ -1,6 +1,6 @@ clear all; -LoadLimeSuite +LoadLimeSuiteNG LimeInitialize(); % open first device @@ -14,8 +14,7 @@ %receive samples, overwrite the same array for i=1:40 - samplesCh0 = LimeReceiveSamples(readCnt,0); % read samples from RX channel 0 - samplesCh1 = LimeReceiveSamples(readCnt,1); % read samples from RX channel 1 + samples = LimeReceiveSamples(readCnt); % read samples from all RX channels end LimeGetStreamStatus() %must run at least 1s to get data rate (B/s) %stop streaming @@ -23,7 +22,7 @@ LimeDestroy(); % close device %plot samples figure(1) -plot(real(samplesCh0)); +plot(samples(1,:)); figure(2) -plot(real(samplesCh1)); +plot(samples(2,:)); diff --git a/plugins/octave/trx.m b/plugins/octave/examples/trx.m similarity index 97% rename from plugins/octave/trx.m rename to plugins/octave/examples/trx.m index a894870f..e79a9d38 100644 --- a/plugins/octave/trx.m +++ b/plugins/octave/examples/trx.m @@ -1,5 +1,5 @@ clear all; -LoadLimeSuite +LoadLimeSuiteNG % generate test signal phase = pi/600; % phase step @@ -27,9 +27,5 @@ figure 2 plot(real(rxSignal2)); %plot last rx LimeGetStreamStatus() %must run at least 1s to get data rate (B/s) -sleep(1); LimeStopStreaming(); % stop streaming LimeDestroy(); % close device - - - diff --git a/plugins/octave/trxTest.ini b/plugins/octave/examples/trxTest.ini similarity index 100% rename from plugins/octave/trxTest.ini rename to plugins/octave/examples/trxTest.ini diff --git a/plugins/octave/tx.m b/plugins/octave/examples/tx.m similarity index 90% rename from plugins/octave/tx.m rename to plugins/octave/examples/tx.m index eaf16312..69d8f7dd 100644 --- a/plugins/octave/tx.m +++ b/plugins/octave/examples/tx.m @@ -1,6 +1,6 @@ clear all; -LoadLimeSuite +LoadLimeSuiteNG %generate test signal phase = pi/6; %phase step @@ -15,15 +15,13 @@ fifoSize = 1024*1024 %set library FIFO size to 1 MSample LimeStartStreaming(fifoSize,"tx0"); % start TX to channel 0 for i=1:100 - LimeTransmitSamples(src,0); % send samples to TX channel 0 + LimeTransmitSamples(src); % send samples to TX end LimeGetStreamStatus() %must run at least 1s to get data rate (B/s) -sleep(1); LimeStopStreaming(); % stop streaming %Waveform playback from device RAM LimeLoopWFMStart(src); % Load samples to device RAM, for looping to TX -sleep(10); LimeLoopWFMStop(); % stop looping TX samples from device RAM LimeDestroy(); % close device diff --git a/plugins/octave/wfm.m b/plugins/octave/examples/wfm.m similarity index 93% rename from plugins/octave/wfm.m rename to plugins/octave/examples/wfm.m index 4627299e..abb7a5fe 100644 --- a/plugins/octave/wfm.m +++ b/plugins/octave/examples/wfm.m @@ -1,5 +1,5 @@ %imports shared library functions -LoadLimeSuite +LoadLimeSuiteNG %Generate waveform phase = pi/8; @@ -7,7 +7,7 @@ %%initialize LimeSuite %if several boards are connected -%deviceList = LimeGetDeviceList(); +deviceList = LimeGetDeviceList() %selectedDevice = 1; %LimeInitialize(deviceList(selectedDevice)); %otherwise initialization uses first available device diff --git a/plugins/octave/loader/.clang-format b/plugins/octave/loader/.clang-format new file mode 100644 index 00000000..460cb9d9 --- /dev/null +++ b/plugins/octave/loader/.clang-format @@ -0,0 +1,2 @@ +SortIncludes: false +DisableFormat: true \ No newline at end of file diff --git a/plugins/octave/loader/LoadLimeSuiteNG.m b/plugins/octave/loader/LoadLimeSuiteNG.m new file mode 100644 index 00000000..b11c7143 --- /dev/null +++ b/plugins/octave/loader/LoadLimeSuiteNG.m @@ -0,0 +1,13 @@ +autoload('LimeInitialize', which('LimeSuiteNG.oct')) +autoload('LimeGetDeviceInfo', which('LimeSuiteNG.oct')) +autoload('LimeDestroy', which('LimeSuiteNG.oct')) +autoload('LimeLoadConfig', which('LimeSuiteNG.oct')) +autoload('LimeStartStreaming', which('LimeSuiteNG.oct')) +autoload('LimeStopStreaming', which('LimeSuiteNG.oct')) +autoload('LimeReceiveSamples', which('LimeSuiteNG.oct')) +autoload('LimeTransmitSamples', which('LimeSuiteNG.oct')) +autoload('LimeGetStreamStatus', which('LimeSuiteNG.oct')) +autoload('LimeLoopWFMStart', which('LimeSuiteNG.oct')) +autoload('LimeLoopWFMStop', which('LimeSuiteNG.oct')) +autoload('LimeGetDeviceList', which('LimeSuiteNG.oct')) +autoload('LimeTransceiveSamples', which('LimeSuiteNG.oct')) \ No newline at end of file diff --git a/src/API/LMS_APIWrapper.cpp b/src/API/LMS_APIWrapper.cpp index 4930d668..9f1017c5 100644 --- a/src/API/LMS_APIWrapper.cpp +++ b/src/API/LMS_APIWrapper.cpp @@ -1876,25 +1876,14 @@ API_EXPORT int CALL_CONV LMS_UploadWFM(lms_device_t* device, const void** sample API_EXPORT int CALL_CONV LMS_EnableTxWFM(lms_device_t* device, unsigned ch, bool active) { - uint16_t regAddr = 0x000D; - uint16_t regValue = 0; - - int status = LMS_WriteFPGAReg(device, 0xFFFF, 1 << (ch / 2)); - if (status != 0) - { - return status; - } - - status = LMS_ReadFPGAReg(device, regAddr, ®Value); - if (status != 0) + LMS_APIDevice* apiDevice = CheckDevice(device); + if (apiDevice == nullptr) { - return status; + return -1; } - regValue = regValue & ~0x6; //clear WFM_LOAD, WFM_PLAY - regValue |= active << 1; - status = LMS_WriteFPGAReg(device, regAddr, regValue); - return status; + OpStatus status = apiDevice->device->EnableTxWaveform(apiDevice->moduleIndex, ch, active); + return OpStatusToReturnCode(status); } API_EXPORT int CALL_CONV LMS_GetProgramModes(lms_device_t* device, lms_name_t* list) diff --git a/src/boards/LMS7002M_SDRDevice.cpp b/src/boards/LMS7002M_SDRDevice.cpp index e30790f4..651f2fdf 100644 --- a/src/boards/LMS7002M_SDRDevice.cpp +++ b/src/boards/LMS7002M_SDRDevice.cpp @@ -1274,4 +1274,22 @@ OpStatus LMS7002M_SDRDevice::LMS7002TestSignalConfigure(LMS7002M* chip, const Ch return OpStatus::Success; } +OpStatus LMS7002M_SDRDevice::EnableTxWaveform(uint8_t moduleIndex, uint8_t channel, bool enabled) +{ + OpStatus status = WriteFPGARegister(0xFFFF, 1 << (channel / 2)); + if (status != OpStatus::Success) + { + return status; + } + + constexpr uint32_t regAddr{ 0x000D }; + + int regValue = ReadFPGARegister(regAddr); + regValue = regValue & ~0x6; //clear WFM_LOAD, WFM_PLAY + regValue |= enabled << 1; + + status = WriteFPGARegister(regAddr, regValue); + return status; +} + } // namespace lime diff --git a/src/boards/LMS7002M_SDRDevice.h b/src/boards/LMS7002M_SDRDevice.h index 477b0bd8..8b503c57 100644 --- a/src/boards/LMS7002M_SDRDevice.h +++ b/src/boards/LMS7002M_SDRDevice.h @@ -120,6 +120,8 @@ class LIME_API LMS7002M_SDRDevice : public SDRDevice OpStatus UploadMemory( eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) override; + OpStatus EnableTxWaveform(uint8_t moduleIndex, uint8_t channel, bool enabled) override; + /// @copydoc FPGA::ReadRegister() virtual int ReadFPGARegister(uint32_t address); /// @copydoc FPGA::WriteRegister() diff --git a/src/boards/MMX8/MM_X8.cpp b/src/boards/MMX8/MM_X8.cpp index 3a00182a..20e9c11b 100644 --- a/src/boards/MMX8/MM_X8.cpp +++ b/src/boards/MMX8/MM_X8.cpp @@ -830,4 +830,14 @@ OpStatus LimeSDR_MMX8::UploadTxWaveform(const StreamConfig& config, uint8_t modu return mSubDevices[moduleIndex]->UploadTxWaveform(config, 0, samples, count); } +OpStatus LimeSDR_MMX8::EnableTxWaveform(uint8_t moduleIndex, uint8_t channel, bool enabled) +{ + if (moduleIndex >= 8) + { + moduleIndex = 0; + } + + return mSubDevices[moduleIndex]->EnableTxWaveform(moduleIndex, channel, enabled); +} + } //namespace lime diff --git a/src/boards/MMX8/MM_X8.h b/src/boards/MMX8/MM_X8.h index 032f870e..ac889762 100644 --- a/src/boards/MMX8/MM_X8.h +++ b/src/boards/MMX8/MM_X8.h @@ -148,6 +148,7 @@ class LimeSDR_MMX8 : public SDRDevice OpStatus MemoryWrite(std::shared_ptr storage, Region region, const void* data) override; OpStatus MemoryRead(std::shared_ptr storage, Region region, void* data) override; OpStatus UploadTxWaveform(const StreamConfig& config, uint8_t moduleIndex, const void** samples, uint32_t count) override; + OpStatus EnableTxWaveform(uint8_t moduleIndex, uint8_t channel, bool enabled) override; private: std::shared_ptr mMainFPGAcomms; diff --git a/src/include/limesuiteng/SDRDevice.h b/src/include/limesuiteng/SDRDevice.h index 34dd1e78..d21fdc0c 100644 --- a/src/include/limesuiteng/SDRDevice.h +++ b/src/include/limesuiteng/SDRDevice.h @@ -488,6 +488,13 @@ class LIME_API SDRDevice /// @return Operation status. virtual OpStatus UploadTxWaveform(const StreamConfig& config, uint8_t moduleIndex, const void** samples, uint32_t count); + /// @brief Enables or disables the transmission of the uploaded waveform + /// @param moduleIndex The index of the device to transmit the waveform from. + /// @param channel The channel to use for the waveform transmission. + /// @param enabled Whether to enable or disable the transmission. + /// @return Operation status. + virtual OpStatus EnableTxWaveform(uint8_t moduleIndex, uint8_t channel, bool enabled) = 0; + /// @copydoc ISPI::SPI() /// @param spiBusAddress The SPI address of the device to use. virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count);