├── LICENSE.MIT ├── LimeSuite_Build_Thunk.h ├── README.md ├── libLimeSuite__proto.m ├── limeSDR.m ├── limeSDR_Simulink.m └── limeSDR_XCVR.m /LICENSE.MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2017 JiangWei 2 | Copyright (c) 2013-2015 Nuand LLC 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LimeSuite_Build_Thunk.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lime/LimeSuite.h 3 | * 4 | * @brief LMS API library 5 | * 6 | * Copyright (C) 2016 Lime Microsystems 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef LMS7_API_H 22 | #define LMS7_API_H 23 | 24 | #include 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #else 30 | /* stdbool.h is not applicable for C++ programs, as the language inherently 31 | * provides the bool type. 32 | * 33 | * Users of Visual Studio 2012 and earlier will need to supply a stdbool.h 34 | * implementation, as it is not included with the toolchain. Visual Studio 2013 35 | * onward supplies this header. 36 | */ 37 | #include 38 | #endif 39 | 40 | #if defined _WIN32 || defined __CYGWIN__ 41 | # include 42 | # define CALL_CONV __cdecl 43 | # ifdef __GNUC__ 44 | # define API_EXPORT __attribute__ ((dllexport)) 45 | # else 46 | # define API_EXPORT __declspec(dllexport) 47 | # endif 48 | #elif defined _DOXYGEN_ONLY_ 49 | /** Marks an API routine to be made visible to the dynamic loader. 50 | * This is OS and/or compiler-specific. */ 51 | # define API_EXPORT 52 | /** Specifies calling convention, if necessary. 53 | * This is OS and/or compiler-specific. */ 54 | # define CALL_CONV 55 | #else 56 | # define API_EXPORT __attribute__ ((visibility ("default"))) 57 | # define CALL_CONV 58 | #endif 59 | 60 | ///Floating point data type 61 | typedef double float_type; 62 | 63 | ///convenience constant for good return code 64 | static const int LMS_SUCCESS = 0; 65 | 66 | /** 67 | * @defgroup FN_INIT Initialization/deinitialization 68 | * 69 | * The functions in this section provide the ability to query available devices, 70 | * initialize them, and deinitialize them. 71 | * @{ 72 | */ 73 | 74 | ///LMS Device handle 75 | typedef void lms_device_t; 76 | 77 | ///Convenience type for fixed length LMS Device information string 78 | typedef char lms_info_str_t[256]; 79 | 80 | /** 81 | * Obtain a list of LMS devices attached to the system 82 | * 83 | * @param[out] dev_list List of available devices 84 | * 85 | * @return number of devices in the list on success, (-1) on failure 86 | */ 87 | API_EXPORT int CALL_CONV LMS_GetDeviceList(lms_info_str_t *dev_list); 88 | 89 | /** 90 | * Opens device specified by the provided ::lms_info_str_t string 91 | * This function should be used to open a device based upon the results of 92 | * LMS_GetDeviceList() 93 | * 94 | * @pre device should be initialized to NULL 95 | * 96 | * @param[out] device Updated with device handle on success 97 | * @param[in] info Device information string. If NULL, the first 98 | * available device will be opened. 99 | * @param[in] args additional arguments. Can be NULL. 100 | * 101 | * @return 0 on success, (-1) on failure 102 | */ 103 | API_EXPORT int CALL_CONV LMS_Open(lms_device_t **device, const lms_info_str_t info, 104 | void* args); 105 | 106 | /** 107 | * Close device 108 | * 109 | * @post device is deallocated and may no longer be used. 110 | * 111 | * @param device Device handle previously obtained by LMS_Open(). 112 | * 113 | * @return 0 on success, (-1) on failure 114 | */ 115 | API_EXPORT int CALL_CONV LMS_Close(lms_device_t *device); 116 | 117 | /** 118 | * Disconnect device but keep configuration cache (device is not deallocated). 119 | * 120 | * @param device Device handle previously obtained by LMS_Open(). 121 | * 122 | * @return 0 on success, (-1) on failure 123 | */ 124 | API_EXPORT int CALL_CONV LMS_Disconnect(lms_device_t *device); 125 | 126 | /** 127 | * Check if device port is opened 128 | * 129 | * @param device Device handle previously obtained by LMS_Open(). 130 | * @param port port index (ignored if device has only 1 port) 131 | * 132 | * @return true(1) if port is open, false (0) if - closed 133 | */ 134 | API_EXPORT bool CALL_CONV LMS_IsOpen(lms_device_t *device, int port); 135 | 136 | /** @} (End FN_INIT) */ 137 | 138 | /** 139 | * @defgroup FN_HIGH_LVL High-level control functions 140 | * 141 | * The functions in this section provide the ability to easily configure the 142 | * device for operation. 143 | * 144 | * @{ 145 | */ 146 | 147 | static const bool LMS_CH_TX = true; ///. files. 100 | % Users of pre-built binaries need not concern themselves with this 101 | % function. 102 | % 103 | % Use of this function requires that: 104 | % - The system contains a MATLAB-compatible compiler 105 | % - Any required libraries, DLLs, headers, etc. are in the search path or current working directory. 106 | % 107 | % For Windows users, the simplest approach is to copy the following 108 | % to this directory: 109 | % - LimeSuite.dll 110 | 111 | if libisloaded('libLimeSuite') == true 112 | unloadlibrary('libLimeSuite'); 113 | end 114 | 115 | arch = computer('arch'); 116 | this_dir = pwd; 117 | proto_output = 'delete_this_file'; 118 | switch arch 119 | case 'win64' 120 | [notfound, warnings] = loadlibrary('LimeSuite', 'LimeSuite_Build_Thunk.h', 'includepath', this_dir, 'alias', 'libLimeSuite', 'notempdir', 'mfilename', proto_output); 121 | case 'glnxa64' 122 | [notfound, warnings] = loadlibrary('libLimeSuite', 'LimeSuite_Build_Thunk.h', 'includepath', this_dir, 'notempdir', 'mfilename', proto_output); 123 | case 'maci64' 124 | [notfound, warnings] = loadlibrary('libLimeSuite.dylib', 'LimeSuite_Build_Thunk.h', 'includepath', this_dir, 'notempdir', 'mfilename', proto_output); 125 | otherwise 126 | error(strcat('Unsupported architecture: ', arch)) 127 | end 128 | 129 | % Remove the generated protofile that we don't want folks to 130 | % use. Our hand-crafted protofile contains workarounds for 131 | % loadlibrary() issues that the autogenerated file does not. 132 | delete 'delete_this_file.m' 133 | 134 | if isempty(notfound) == false 135 | fprintf('\nMissing functions:\n'); 136 | disp(notfound.'); 137 | error('Failed to find the above functions in libLimeSuite.'); 138 | end 139 | 140 | if isempty(warnings) == false 141 | warning('Encountered the following warnings while loading libLimeSuite:\n%s\n', warnings); 142 | end 143 | end 144 | function [major, minor, patch] = version() 145 | major = 0; 146 | minor = 0; 147 | patch = 1; 148 | end 149 | function [version_string] = library_version() 150 | limeSDR.load_library(); 151 | version_string=char(calllib('libLimeSuite', 'LMS_GetLibraryVersion')); 152 | end 153 | function devs = devices 154 | limeSDR.load_library(); 155 | pdevlist = libpointer('int8Ptr'); 156 | 157 | [count, ~] = calllib('libLimeSuite', 'LMS_GetDeviceList', pdevlist); 158 | if count < 0 159 | error(calllib('libLimeSuite', 'LMS_GetLastErrorMessage')); 160 | end 161 | 162 | if count > 0 163 | devs = repmat(struct('deviceName', [], 'expansionName', [], 'firmwareVersion', [], 'hardwareVersion', [], 'protocolVersion', [], 'boardSerialNumber', [], 'gatewareVersion', [], 'gatewareRevision', [], 'gatewareTargetBoard', []), 1, count); 164 | for x = 0:(count-1) 165 | dptr = libpointer('voidPtrPtr'); 166 | calllib('libLimeSuite', 'LMS_Open', dptr,pdevlist+x,0); 167 | dinfo=libstruct('lms_dev_info_t',calllib('libLimeSuite', 'LMS_GetDeviceInfo', dptr)); 168 | devs(x+1).deviceName=char(dinfo.deviceName); 169 | devs(x+1).expansionName=char(dinfo.expansionName); 170 | devs(x+1).firmwareVersion=char(dinfo.firmwareVersion); 171 | devs(x+1).hardwareVersion=char(dinfo.hardwareVersion); 172 | devs(x+1).protocolVersion=char(dinfo.protocolVersion); 173 | devs(x+1).gatewareVersion=char(dinfo.gatewareVersion); 174 | devs(x+1).gatewareRevision=char(dinfo.gatewareRevision); 175 | devs(x+1).gatewareTargetBoard=char(dinfo.gatewareTargetBoard); 176 | calllib('libLimeSuite', 'LMS_Close', dptr); 177 | end 178 | else 179 | devs = []; 180 | end 181 | end 182 | end 183 | 184 | methods(Static, Access = private) 185 | function load_library 186 | if libisloaded('libLimeSuite') == false 187 | arch = computer('arch'); 188 | switch arch 189 | case 'win64' 190 | [notfound, warnings] = loadlibrary('LimeSuite', @libLimeSuite__proto, 'alias', 'libLimeSuite'); 191 | case 'glnxa64' 192 | [notfound, warnings] = loadlibrary('libLimeSuite', @libLimeSuite__proto); 193 | case 'maci64' 194 | [notfound, warnings] = loadlibrary('libLimeSuite.dylib', @libLimeSuite__proto); 195 | otherwise 196 | error(strcat('Unsupported architecture: ', arch)) 197 | end 198 | 199 | if isempty(notfound) == false 200 | error('Failed to find functions in libLimeSuite.'); 201 | end 202 | 203 | if isempty(warnings) == false 204 | warning('Encountered the following warnings while loading libLimeSuite:\n%s\n', warnings); 205 | end 206 | end 207 | 208 | end 209 | end 210 | 211 | methods 212 | function obj = limeSDR(serial) 213 | 214 | if nargin < 1 215 | serial = ''; 216 | end 217 | 218 | limeSDR.load_library(); 219 | %TODO serial select device 220 | pdevlist = libpointer('int8Ptr'); 221 | 222 | [count, ~] = calllib('libLimeSuite', 'LMS_GetDeviceList', pdevlist); 223 | if count < 0 224 | error(calllib('libLimeSuite', 'LMS_GetLastErrorMessage')); 225 | end 226 | 227 | dptr = libpointer('voidPtrPtr'); 228 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_Open', dptr,pdevlist,0)); 229 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_Init', dptr)); 230 | obj.device=dptr; 231 | 232 | dinfo=libstruct('lms_dev_info_t',calllib('libLimeSuite', 'LMS_GetDeviceInfo', obj.device)); 233 | obj.info.deviceName=char(dinfo.deviceName); 234 | obj.info.expansionName=char(dinfo.expansionName); 235 | obj.info.firmwareVersion=char(dinfo.firmwareVersion); 236 | obj.info.hardwareVersion=char(dinfo.hardwareVersion); 237 | obj.info.protocolVersion=char(dinfo.protocolVersion); 238 | obj.info.gatewareVersion=char(dinfo.gatewareVersion); 239 | obj.info.gatewareRevision=char(dinfo.gatewareRevision); 240 | obj.info.gatewareTargetBoard=char(dinfo.gatewareTargetBoard); 241 | 242 | obj.versions.library_version=char(calllib('libLimeSuite', 'LMS_GetLibraryVersion')); 243 | 244 | obj.rx0 = limeSDR_XCVR(obj, 'RX',0); 245 | obj.rx1 = limeSDR_XCVR(obj, 'RX',1); 246 | obj.tx0 = limeSDR_XCVR(obj, 'TX',0); 247 | obj.tx1 = limeSDR_XCVR(obj, 'TX',1); 248 | obj.running= false; 249 | 250 | end 251 | 252 | function delete(obj) 253 | 254 | %disp('Delete limeSDR called'); 255 | if isempty(obj.device) == false 256 | calllib('libLimeSuite', 'LMS_Close', obj.device); 257 | end 258 | 259 | end 260 | 261 | function [samples, timestamp_out, actual_count, overrun] = receive(obj, num_samples, chan,timeout_ms, timestamp_in) 262 | 263 | if nargin < 3 264 | chan = 0; 265 | end 266 | 267 | if nargin < 4 268 | timeout_ms = 1000; 269 | end 270 | 271 | if nargin < 5 272 | timestamp_in = 0; 273 | end 274 | 275 | if ~obj.running 276 | error('please start device'); 277 | end 278 | 279 | f32=single(zeros(2*num_samples, 1)); 280 | 281 | metad = libstruct('lms_stream_meta_t'); 282 | 283 | if timestamp_in == 0 284 | metad.waitForTimestamp=false; 285 | else 286 | metad.waitForTimestamp=true; 287 | end 288 | metad.timestamp=timestamp_in; 289 | if chan==0 290 | rx_stream=obj.rx0_stream; 291 | if isempty(rx_stream) 292 | error('rx0 stream not enabled'); 293 | end 294 | else 295 | rx_stream=obj.rx1_stream; 296 | 297 | if isempty(rx_stream) 298 | error('rx1 stream not enabled'); 299 | end 300 | end 301 | 302 | [actual_count, ~, f32, ~]=calllib('libLimeSuite', 'LMS_RecvStream', rx_stream,f32,num_samples,metad,timeout_ms); 303 | samples=(double(f32(1:2:end)) + double(f32(2:2:end))*1j); 304 | timestamp_out = metad.timestamp; 305 | end 306 | 307 | function transmit(obj, samples,chan, timeout_ms, timestamp_in) 308 | if nargin < 3 309 | chan = 0; 310 | end 311 | 312 | if nargin < 4 313 | timeout_ms = 1000; 314 | end 315 | 316 | if nargin < 5 317 | timestamp_in = 0; 318 | end 319 | 320 | if ~obj.running 321 | error('please start device'); 322 | end 323 | 324 | metad = libstruct('lms_stream_meta_t'); 325 | 326 | if timestamp_in == 0 327 | metad.waitForTimestamp=false; 328 | else 329 | metad.waitForTimestamp=true; 330 | end 331 | metad.timestamp=timestamp_in; 332 | 333 | 334 | if chan==0 335 | tx_stream=obj.tx0_stream; 336 | if isempty(tx_stream) 337 | error('tx0 stream not enabled'); 338 | end 339 | else 340 | tx_stream=obj.tx1_stream; 341 | if isempty(tx_stream) 342 | error('tx1 stream not enabled'); 343 | end 344 | end 345 | 346 | f32 = zeros(2 * length(samples), 1, 'single'); 347 | f32(1:2:end) = real(samples); 348 | f32(2:2:end) = imag(samples); 349 | 350 | calllib('libLimeSuite', 'LMS_SendStream', tx_stream,f32,length(samples),metad,timeout_ms); 351 | end 352 | 353 | function start(obj) 354 | if isempty(obj.rx0_stream)==false 355 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_StartStream', obj.rx0_stream)); 356 | end 357 | 358 | if isempty(obj.rx1_stream)==false 359 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_StartStream', obj.rx1_stream)); 360 | end 361 | 362 | if isempty(obj.tx0_stream)==false 363 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_StartStream', obj.tx0_stream)); 364 | end 365 | 366 | if isempty(obj.tx1_stream)==false 367 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_StartStream', obj.tx1_stream)); 368 | end 369 | 370 | obj.running=true; 371 | end 372 | 373 | function stop(obj) 374 | 375 | if obj.rx0.running 376 | obj.rx0.disable(); 377 | end 378 | if obj.rx1.running 379 | obj.rx1.disable(); 380 | end 381 | 382 | if obj.tx0.running 383 | obj.tx0.disable(); 384 | end 385 | 386 | if obj.tx1.running 387 | obj.tx1.disable(); 388 | end 389 | 390 | if isempty(obj.rx0_stream)==false 391 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_DestroyStream',obj.device, obj.rx0_stream)); 392 | obj.rx0_stream=[]; 393 | end 394 | 395 | if isempty(obj.rx1_stream)==false 396 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_DestroyStream',obj.device, obj.rx1_stream)); 397 | obj.rx1_stream=[]; 398 | end 399 | 400 | if isempty(obj.tx0_stream)==false 401 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_DestroyStream',obj.device, obj.tx0_stream)); 402 | obj.tx0_stream=[]; 403 | end 404 | 405 | if isempty(obj.tx1_stream)==false 406 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_DestroyStream',obj.device, obj.tx1_stream)); 407 | obj.tx1_stream=[]; 408 | end 409 | 410 | end 411 | 412 | function loadconfig(obj,filename) 413 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_LoadConfig', obj.device,filename)); 414 | end 415 | end 416 | 417 | end -------------------------------------------------------------------------------- /limeSDR_Simulink.m: -------------------------------------------------------------------------------- 1 | classdef limeSDR_Simulink < matlab.System & ... 2 | matlab.system.mixin.Propagates & ... 3 | matlab.system.mixin.CustomIcon & ... 4 | matlab.system.mixin.internal.SampleTime 5 | %% Properties 6 | properties 7 | verbosity = 'Info' % limeSDR verbosity 8 | 9 | rx_frequency = 915e6; % Frequency [100e6, 3.8e9] 10 | 11 | rx0_gain =0.5; % Gain [0, 1] 12 | rx1_gain =0.5; % Gain [0, 1] 13 | 14 | tx_frequency = 920e6; % Frequency [100e6, 3.8e9] 15 | 16 | tx0_gain =0.5; % Gain [0, 1] 17 | tx1_gain =0.5; % Gain [0, 1] 18 | end 19 | 20 | 21 | 22 | properties(Nontunable) 23 | device_string = ''; % Device specification string 24 | 25 | rx_samplerate = 3e6; % Sample rate 26 | rx_step_size = 16384; % Frame 27 | rx_timeout_ms = 5000; % Stream timeout (ms) 28 | 29 | rx0_bandwidth = '1.5'; % LPF Bandwidth (MHz) 30 | rx0_antenna = 2; %1=LNA_H 2=LNA_L 3=LNA_W 31 | 32 | rx1_bandwidth = '1.5'; % LPF Bandwidth (MHz) 33 | rx1_antenna = 2;%1=LNA_H 2=LNA_L 3=LNA_W 34 | 35 | 36 | tx_samplerate = 3e6; % Sample rate 37 | tx_step_size = 16384; % Frame 38 | tx_timeout_ms = 5000; % Stream timeout (ms) 39 | 40 | tx0_bandwidth = '1.5'; % LPF Bandwidth (MHz) 41 | tx0_antenna = 1; %1=TX_PATH1 2=TX_PATH2 42 | 43 | 44 | tx1_bandwidth = '1.5'; % LPF Bandwidth (MHz) 45 | tx1_antenna = 1;%1=TX_PATH1 2=TX_PATH2 46 | 47 | end 48 | 49 | properties(Logical, Nontunable) 50 | enable_rx0 = true; % Enable Receiver 51 | enable_rx1 = false; % Enable Receiver 52 | 53 | enable_tx0 = false; % Enable Transmitter 54 | enable_tx1 = false; % Enable Transmitter 55 | 56 | end 57 | 58 | properties(Hidden, Transient) 59 | rx0_bandwidthSet = matlab.system.StringSet({ ... 60 | '1.5', '1.75', '2.5', '2.75', ... 61 | '3', '3.84', '5', '5.5', ... 62 | '6', '7', '8.75', '10', ... 63 | '12', '14', '20', '28' ... 64 | }); 65 | 66 | rx1_bandwidthSet = matlab.system.StringSet({ ... 67 | '1.5', '1.75', '2.5', '2.75', ... 68 | '3', '3.84', '5', '5.5', ... 69 | '6', '7', '8.75', '10', ... 70 | '12', '14', '20', '28' ... 71 | }); 72 | 73 | tx0_bandwidthSet = matlab.system.StringSet({ ... 74 | '1.5', '1.75', '2.5', '2.75', ... 75 | '3', '3.84', '5', '5.5', ... 76 | '6', '7', '8.75', '10', ... 77 | '12', '14', '20', '28' ... 78 | }); 79 | 80 | tx1_bandwidthSet = matlab.system.StringSet({ ... 81 | '1.5', '1.75', '2.5', '2.75', ... 82 | '3', '3.84', '5', '5.5', ... 83 | '6', '7', '8.75', '10', ... 84 | '12', '14', '20', '28' ... 85 | }); 86 | end 87 | 88 | properties (Access = private) 89 | device = [] 90 | running 91 | end 92 | 93 | %% Static Methods 94 | methods (Static, Access = protected) 95 | function groups = getPropertyGroupsImpl 96 | device_section_group = matlab.system.display.SectionGroup(... 97 | 'Title', 'Device', ... 98 | 'PropertyList', {'device_string' } ... 99 | ); 100 | 101 | %%RX 102 | 103 | rx0_group = matlab.system.display.Section(... 104 | 'Title','RX0 parameters',... 105 | 'PropertyList',{'enable_rx0','rx0_antenna','rx0_bandwidth','rx0_gain'}); 106 | 107 | rx1_group = matlab.system.display.Section(... 108 | 'Title','RX1 parameters',... 109 | 'PropertyList',{'enable_rx1','rx1_antenna','rx1_bandwidth','rx1_gain'}); 110 | 111 | rx_stream_section = matlab.system.display.Section(... 112 | 'Title', 'RX config', ... 113 | 'PropertyList', {'rx_frequency','rx_samplerate', 'rx_timeout_ms', 'rx_step_size', } ... 114 | ); 115 | 116 | rx_section_group = matlab.system.display.SectionGroup(... 117 | 'Title', 'RX Configuration', ... 118 | 'Sections', [ rx0_group, rx1_group, rx_stream_section] ... 119 | ); 120 | 121 | %%TX 122 | 123 | tx0_group = matlab.system.display.Section(... 124 | 'Title','TX0 parameters',... 125 | 'PropertyList',{'enable_tx0','tx0_antenna','tx0_bandwidth','tx0_gain'}); 126 | 127 | tx1_group = matlab.system.display.Section(... 128 | 'Title','TX1 parameters',... 129 | 'PropertyList',{'enable_tx1','tx1_antenna','tx1_bandwidth','tx1_gain'}); 130 | 131 | tx_stream_section = matlab.system.display.Section(... 132 | 'Title', 'TX config', ... 133 | 'PropertyList', {'tx_frequency','tx_samplerate', 'tx_timeout_ms', 'tx_step_size', } ... 134 | ); 135 | 136 | tx_section_group = matlab.system.display.SectionGroup(... 137 | 'Title', 'TX Configuration', ... 138 | 'Sections', [ tx0_group, tx1_group, tx_stream_section] ... 139 | ); 140 | 141 | % groups = [device_section_group,rx0_section_group,rx1_section_group, tx0_section_group, tx1_section_group ]; 142 | groups = [device_section_group,rx_section_group,tx_section_group ]; 143 | 144 | end 145 | 146 | function simMode = getSimulateUsingImpl 147 | % Return only allowed simulation mode in System block dialog 148 | simMode = 'Interpreted execution'; 149 | end 150 | 151 | function flag = showSimulateUsingImpl 152 | % Return false if simulation mode hidden in System block dialog 153 | flag = false; 154 | end 155 | 156 | function header = getHeaderImpl 157 | text = 'This block provides access to a LimeSDR device via limeSDR MATLAB bindings.'; 158 | header = matlab.system.display.Header('limeSDR_Simulink', ... 159 | 'Title', 'limeSDR', 'Text', text ... 160 | ); 161 | end 162 | end 163 | 164 | methods (Access = protected) 165 | %% Output setup 166 | function count = getNumOutputsImpl(obj) 167 | if obj.enable_rx0 == true 168 | count = 1; 169 | else 170 | count = 0; 171 | end 172 | 173 | if obj.enable_rx1 == true 174 | count = count + 1; 175 | end 176 | end 177 | 178 | function varargout = getOutputNamesImpl(obj) 179 | if obj.enable_rx0 == true 180 | varargout{1} = 'RX0 Samples'; 181 | n = 2; 182 | else 183 | n = 1; 184 | end 185 | 186 | if obj.enable_rx1 ==true 187 | varargout{n} = 'RX1 Samples'; 188 | n=n+1; 189 | end 190 | end 191 | 192 | function varargout = getOutputDataTypeImpl(obj) 193 | if obj.enable_rx0 == true 194 | varargout{1} = 'double'; % RX0 Samples 195 | n = 2; 196 | else 197 | n = 1; 198 | end 199 | 200 | if obj.enable_rx1 == true 201 | varargout{n} = 'double'; % RX1 Samples 202 | end 203 | 204 | end 205 | 206 | function st = getSampleTimeImpl(obj) 207 | st=1/obj.rx_samplerate*obj.rx_step_size; 208 | end 209 | 210 | 211 | function varargout = getOutputSizeImpl(obj) 212 | if obj.enable_rx0 == true 213 | varargout{1} = [obj.rx_step_size 1]; % RX0 Samples 214 | n = 2; 215 | else 216 | n = 1; 217 | end 218 | 219 | if obj.enable_rx1 == true 220 | varargout{n} = [obj.rx_step_size 1]; % RX1 Samples 221 | n=n+1; 222 | end 223 | end 224 | 225 | function varargout = isOutputComplexImpl(obj) 226 | if obj.enable_rx0 == true 227 | varargout{1} = true; % RX0 Samples 228 | n = 2; 229 | else 230 | n = 1; 231 | end 232 | 233 | if obj.enable_rx1 == true 234 | varargout{n} = true; % RX1 Samples 235 | n = n + 1; 236 | end 237 | end 238 | 239 | function varargout = isOutputFixedSizeImpl(obj) 240 | if obj.enable_rx0 == true 241 | varargout{1} = true; % RX0 Samples 242 | n = 2; 243 | else 244 | n = 1; 245 | end 246 | 247 | if obj.enable_rx1 == true 248 | varargout{n} = true; % RX1 Samples 249 | end 250 | 251 | end 252 | 253 | 254 | %% Input setup 255 | function count = getNumInputsImpl(obj) 256 | if obj.enable_tx0 == true 257 | count = 1; 258 | else 259 | count = 0; 260 | end 261 | 262 | if obj.enable_tx1 == true 263 | count=count+1; 264 | end 265 | end 266 | 267 | function varargout = getInputNamesImpl(obj) 268 | if obj.enable_tx0 == true 269 | varargout{1} = 'TX0 Samples'; 270 | n = 2; 271 | else 272 | n = 1; 273 | end 274 | 275 | if obj.enable_tx1 == true 276 | varargout{n} = 'TX1 Samples'; 277 | end 278 | 279 | end 280 | 281 | %% Property and Execution Handlers 282 | function icon = getIconImpl(~) 283 | icon = sprintf('LimeSDR'); 284 | end 285 | 286 | function setupImpl(obj) 287 | 288 | %% Device setup 289 | obj.device = limeSDR(obj.device_string); 290 | 291 | %% RX0 Setup 292 | if obj.enable_rx0 == true 293 | obj.device.rx0.frequency = obj.rx_frequency; 294 | obj.device.rx0.gain = obj.rx0_gain; 295 | obj.device.rx0.samplerate=obj.rx_samplerate; 296 | obj.device.rx0.antenna=obj.rx0_antenna; 297 | end 298 | 299 | %% RX1 Setup 300 | if obj.enable_rx1 == true 301 | obj.device.rx1.frequency = obj.rx_frequency; 302 | obj.device.rx1.gain = obj.rx1_gain; 303 | obj.device.rx1.samplerate=obj.rx_samplerate; 304 | obj.device.rx1.antenna=obj.rx1_antenna; 305 | end 306 | 307 | %% TX0 Setup 308 | if obj.enable_tx0 == true 309 | obj.device.tx0.frequency = obj.tx_frequency; 310 | obj.device.tx0.samplerate=obj.tx_samplerate; 311 | obj.device.tx0.gain = obj.tx0_gain; 312 | obj.device.tx0.antenna=obj.tx0_antenna; 313 | end 314 | 315 | if obj.enable_tx1 == true 316 | %% TX1 Setup 317 | obj.device.tx1.frequency = obj.tx_frequency; 318 | obj.device.tx1.samplerate=obj.tx_samplerate; 319 | obj.device.tx1.gain = obj.tx1_gain; 320 | obj.device.tx1.antenna=obj.tx1_antenna; 321 | end 322 | obj.running=false; 323 | 324 | end 325 | 326 | function releaseImpl(obj) 327 | delete(obj.device); 328 | end 329 | 330 | function resetImpl(obj) 331 | obj.device.stop(); 332 | end 333 | 334 | % Perform a read of received samples and an 'overrun' array that denotes whether 335 | % the associated samples is invalid due to a detected overrun. 336 | function varargout = stepImpl(obj, varargin) 337 | varargout = {}; 338 | 339 | if obj.enable_rx0 == true 340 | if obj.device.rx0.running == false 341 | obj.device.rx0.enable(); 342 | end 343 | end 344 | 345 | if obj.enable_rx1 == true 346 | if obj.device.rx1.running == false 347 | obj.device.rx1.enable(); 348 | end 349 | end 350 | 351 | if obj.enable_tx0 == true 352 | if obj.device.tx0.running == false 353 | obj.device.tx0.enable(); 354 | end 355 | end 356 | 357 | if obj.enable_tx1 == true 358 | if obj.device.tx1.running == false 359 | obj.device.tx1.enable(); 360 | end 361 | end 362 | 363 | if ~obj.running 364 | obj.device.start(); 365 | obj.running=true; 366 | end 367 | 368 | if obj.enable_rx0 == true 369 | 370 | [rx_samples0, ~] = obj.device.receive(obj.rx_step_size,0); 371 | varargout{1} = rx_samples0; 372 | out_idx = 2; 373 | else 374 | out_idx = 1; 375 | end 376 | 377 | if obj.enable_rx1 == true 378 | 379 | [rx_samples1, ~] = obj.device.receive(obj.rx_step_size,1); 380 | 381 | varargout{out_idx} = rx_samples1; 382 | end 383 | 384 | if obj.enable_tx0 == true 385 | obj.device.transmit(varargin{1},0); 386 | in_idx=2; 387 | else 388 | in_idx=1; 389 | end 390 | 391 | if obj.enable_tx1 == true 392 | if obj.device.tx1.running == false 393 | obj.device.tx1.start(); 394 | end 395 | obj.device.transmit(varargin{in_idx},1); 396 | end 397 | end 398 | 399 | function processTunedPropertiesImpl(obj) 400 | 401 | %% RX Properties 402 | 403 | if isChangedProperty(obj, 'rx_frequency') 404 | if obj.enable_rx0 ==true 405 | obj.device.rx0.frequency = obj.rx_frequency; 406 | end 407 | 408 | if obj.enable_rx1 ==true 409 | obj.device.rx1.frequency = obj.rx_frequency; 410 | end 411 | %disp('Updated RX frequency'); 412 | end 413 | 414 | if obj.enable_rx0 ==true 415 | if isChangedProperty(obj, 'rx0_gain') 416 | obj.device.rx0.gain = obj.rx0_gain; 417 | %disp('Updated RX0 gain'); 418 | end 419 | end 420 | 421 | if obj.enable_rx1 ==true 422 | if isChangedProperty(obj, 'rx1_gain') 423 | obj.device.rx1.gain = obj.rx1_gain; 424 | %disp('Updated RX1 gain'); 425 | end 426 | end 427 | %% TX Properties 428 | 429 | if isChangedProperty(obj, 'tx_frequency') 430 | if obj.enable_tx0 ==true 431 | obj.device.tx0.frequency = obj.tx_frequency; 432 | end 433 | 434 | if obj.enable_tx1 ==true 435 | obj.device.tx1.frequency = obj.tx_frequency; 436 | end 437 | %disp('Updated TX frequency'); 438 | end 439 | 440 | if obj.enable_tx0 ==true 441 | if isChangedProperty(obj, 'tx0_frequency') 442 | obj.device.tx0.frequency = obj.tx0_frequency; 443 | %disp('Updated TX frequency'); 444 | end 445 | 446 | if isChangedProperty(obj, 'tx0_gain') 447 | obj.device.tx0.gain = obj.tx0_gain; 448 | %disp('Updated TX VGA1 gain'); 449 | end 450 | 451 | 452 | end 453 | 454 | 455 | if obj.enable_tx1 ==true 456 | if isChangedProperty(obj, 'tx1_gain') 457 | obj.device.tx1.gain = obj.tx1_gain; 458 | %disp('Updated TX VGA1 gain'); 459 | end 460 | end 461 | 462 | end 463 | 464 | function validatePropertiesImpl(obj) 465 | if obj.enable_rx0 == false && obj.enable_tx0 == false && obj.enable_rx1 == false && obj.enable_tx1 == false 466 | warning('LimeSDR RX or TX is enabled. One or both should be enabled.'); 467 | end 468 | 469 | %% Validate RX properties 470 | 471 | if obj.rx_timeout_ms < 0 472 | error('rx_timeout_ms must be >= 0.'); 473 | end 474 | 475 | if obj.rx_step_size <= 0 476 | error('rx_step_size must be > 0.'); 477 | end 478 | 479 | if obj.rx_samplerate < 160.0e3 480 | error('rx_samplerate must be >= 160 kHz.'); 481 | elseif obj.rx_samplerate > 40e6 482 | error('rx_samplerate must be <= 40 MHz.') 483 | end 484 | 485 | if obj.rx_frequency < 100e3 486 | error('rx_frequency must be >= 100 kHz'); 487 | elseif obj.rx_frequency > 3.8e9 488 | error('rx_frequency must be <= 3.8 GHz.'); 489 | end 490 | 491 | if obj.rx0_gain < 0 492 | error('rx0_gain gain must be >= 0.'); 493 | elseif obj.rx0_gain > 1 494 | error('rx0_gain gain must be <= 1.'); 495 | end 496 | 497 | if obj.rx0_antenna <0 498 | error('rx0_antenna must be >= 0.'); 499 | elseif obj.rx0_antenna >3 500 | error('rx0_antenna must be <= 3.'); 501 | end 502 | 503 | 504 | if obj.rx1_gain < 0 505 | error('rx1_gain gain must be >= 0.'); 506 | elseif obj.rx1_gain > 1 507 | error('rx1_gain gain must be <= 1.'); 508 | end 509 | 510 | if obj.rx1_antenna <0 511 | error('rx1_antenna must be >= 0.'); 512 | elseif obj.rx1_antenna >3 513 | error('rx1_antenna must be <= 3.'); 514 | end 515 | 516 | %% Validate TX0 Properties 517 | 518 | if obj.tx_timeout_ms < 0 519 | error('tx_timeout_ms must be >= 0.'); 520 | end 521 | 522 | if obj.tx_step_size <= 0 523 | error('tx_step_size must be > 0.'); 524 | end 525 | 526 | if obj.tx_samplerate < 160.0e3 527 | error('tx_samplerate must be >= 160 kHz.'); 528 | elseif obj.tx_samplerate > 40e6 529 | error('tx_samplerate must be <= 40 MHz.') 530 | end 531 | 532 | if obj.tx_frequency < 100e6; 533 | error('tx_frequency must be >= 100 kHz.'); 534 | elseif obj.tx_frequency > 3.8e9 535 | error('tx_frequency must be <= 3.8 GHz.'); 536 | end 537 | 538 | if obj.tx0_gain < 0 539 | error('tx_vga2 gain must be >= 0.'); 540 | elseif obj.tx0_gain > 1 541 | error('tx_vga2 gain must be <= 1.'); 542 | end 543 | 544 | if obj.tx0_antenna <0 545 | error('rx0_antenna must be >= 0.'); 546 | elseif obj.tx0_antenna >2 547 | error('rx0_antenna must be <= 2.'); 548 | end 549 | 550 | if obj.tx1_gain < 0 551 | error('tx1_gain gain must be >= 0.'); 552 | elseif obj.tx1_gain > 1 553 | error('tx1_gain gain must be <= 1.'); 554 | end 555 | 556 | if obj.tx1_antenna <0 557 | error('tx1_antenna must be >= 0.'); 558 | elseif obj.tx1_antenna >2 559 | error('tx1_antenna must be <= 2.'); 560 | end 561 | 562 | end 563 | end 564 | end 565 | -------------------------------------------------------------------------------- /limeSDR_XCVR.m: -------------------------------------------------------------------------------- 1 | % 2 | % Copyright (c) 2017 JiangWei 3 | % Copyright (c) 2015 Nuand LLC 4 | % 5 | % Permission is hereby granted, free of charge, to any person obtaining a copy 6 | % of this software and associated documentation files (the "Software"), to deal 7 | % in the Software without restriction, including without limitation the rights 8 | % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | % copies of the Software, and to permit persons to whom the Software is 10 | % furnished to do so, subject to the following conditions: 11 | % 12 | % The above copyright notice and this permission notice shall be included in 13 | % all copies or substantial portions of the Software. 14 | % 15 | % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | % THE SOFTWARE. 22 | % 23 | classdef limeSDR_XCVR < handle 24 | 25 | properties(Dependent = true) 26 | samplerate % Samplerate. Must be within 160 kHz and 40 MHz. A 2-3 MHz minimum is suggested unless external filters are being used. 27 | frequency % Frequency. Must be within [237.5 MHz, 3.8 GHz] when no XB-200 is attached, or [0 GHz, 3.8 GHz] when an XB-200 is attached. 28 | bandwidth % LPF bandwidth seting. This is rounded to the nearest of the available discrete settings. It is recommended to set this and read back the actual value. 29 | antenna 30 | gain 31 | channal 32 | end 33 | 34 | properties(SetAccess = private) 35 | running % Denotes whether or not the module is enabled to stream samples. 36 | timestamp % Provides a coarse readback of the timestamp counter. 37 | end 38 | 39 | properties (Access = private) 40 | 41 | 42 | 43 | end 44 | 45 | properties(SetAccess = immutable, Hidden=true) 46 | limesdr % Associated limeSDR device handle 47 | isTx % Module specifier (as a libLimeSuite enum) 48 | chan 49 | end 50 | 51 | methods 52 | 53 | function set.samplerate(obj, val) 54 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_SetSampleRate', obj.limesdr.device,val,0)); 55 | end 56 | 57 | function samplerate_val = get.samplerate(obj) 58 | host_Hz=libpointer('doublePtr',0); 59 | rf_Hz=libpointer('doublePtr',0); 60 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_GetSampleRate', obj.limesdr.device,obj.isTx,obj.chan,host_Hz,rf_Hz)); 61 | samplerate_val=host_Hz.value; 62 | end 63 | 64 | function set.frequency(obj, val) 65 | % limeSDR.check_status(calllib('libLimeSuite', 'LMS_SetLOFrequency', obj.limesdr.device,obj.isTx,obj.chan,val)); 66 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_SetLOFrequency', obj.limesdr.device,obj.isTx,0,val)); 67 | end 68 | 69 | function freq_val = get.frequency(obj) 70 | freq_hz=libpointer('doublePtr',0); 71 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_GetLOFrequency', obj.limesdr.device,obj.isTx,obj.chan,freq_hz)); 72 | freq_val=freq_hz.value; 73 | end 74 | 75 | function set.bandwidth(obj, val) 76 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_SetLPFBW', obj.limesdr.device,obj.isTx,obj.chan,val)); 77 | end 78 | 79 | function bw_val = get.bandwidth(obj) 80 | bw=libpointer('doublePtr',0); 81 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_GetLPFBW', obj.limesdr.device,obj.isTx,obj.chan,bw)); 82 | bw_val=bw.value; 83 | end 84 | 85 | function set.antenna(obj,index) 86 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_SetAntenna', obj.limesdr.device,obj.isTx,obj.chan,index)); 87 | end 88 | 89 | function ant_index = get.antenna(obj) 90 | ant_index= calllib('libLimeSuite', 'LMS_GetAntenna', obj.limesdr.device,obj.isTx,0); 91 | end 92 | 93 | function set.gain(obj,val) 94 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_SetGaindB', obj.limesdr.device,obj.isTx,obj.chan,val)); 95 | end 96 | 97 | function val = get.gain(obj) 98 | gain_val=libpointer('doublePtr',0); 99 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_GetGaindB', obj.limesdr.device,obj.isTx,obj.chan,gain_val)); 100 | val=gain_val.value; 101 | end 102 | 103 | function obj = limeSDR_XCVR(dev, dir, chan) 104 | 105 | if nargin < 3 106 | chan = 0; 107 | end 108 | 109 | if strcmpi(dir,'RX') == false && strcmpi(dir,'TX') == false 110 | error('Invalid direction specified'); 111 | end 112 | 113 | obj.chan=chan; 114 | obj.limesdr=dev; 115 | 116 | if strcmpi(dir,'RX') == true 117 | obj.isTx = 0; 118 | obj.antenna=2;%LNA_L 119 | else 120 | obj.isTx = 1; 121 | obj.antenna=1;%TX_PATH1 122 | end 123 | obj.samplerate = 3e6; 124 | obj.frequency = 100e6; 125 | % obj.bandwidth = 30e6; 126 | 127 | obj.running = false; 128 | 129 | end 130 | 131 | function enable(obj) 132 | obj.running = true; 133 | 134 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_EnableChannel', obj.limesdr.device,obj.isTx,obj.chan,1)); 135 | stream=libstruct('lms_stream_t'); 136 | 137 | stream.isTx=obj.isTx; 138 | stream.channel=obj.chan; 139 | stream.fifoSize=1024*128; 140 | stream.throughputVsLatency=1.0; 141 | 142 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_SetupStream',obj.limesdr.device, stream)); 143 | 144 | if(obj.isTx) 145 | if obj.chan==0 146 | obj.limesdr.tx0_stream=stream; 147 | else 148 | obj.limesdr.tx1_stream=stream; 149 | end 150 | 151 | else 152 | if obj.chan==0 153 | obj.limesdr.rx0_stream=stream; 154 | else 155 | obj.limesdr.rx1_stream=stream; 156 | end 157 | end 158 | 159 | end 160 | 161 | function disable(obj) 162 | if(obj.isTx) 163 | if obj.chan==0 164 | stream=obj.limesdr.tx0_stream; 165 | else 166 | stream=obj.limesdr.tx1_stream; 167 | end 168 | else 169 | if obj.chan==0 170 | stream=obj.limesdr.rx0_stream; 171 | else 172 | stream=obj.limesdr.rx1_stream; 173 | end 174 | end 175 | 176 | if ~isempty(stream) 177 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_StopStream', stream)); 178 | end 179 | 180 | limeSDR.check_status(calllib('libLimeSuite', 'LMS_EnableChannel', obj.limesdr.device,obj.isTx,obj.chan,0)); 181 | obj.running = false; 182 | 183 | end 184 | 185 | end 186 | end --------------------------------------------------------------------------------