diff --git a/+adi/+AD9081/Base.m b/+adi/+AD9081/Base.m index b7e9c1fc..2e7295fe 100644 --- a/+adi/+AD9081/Base.m +++ b/+adi/+AD9081/Base.m @@ -5,6 +5,14 @@ matlabshared.libiio.base %AD9081 Base Class + properties (Dependent) + %SamplingRate Sampling Rate + % Baseband sampling rate in Hz, specified as a scalar + % in samples per second. This value is only readable once + % connected to hardware + SamplingRate + end + properties (Nontunable) %SamplesPerFrame Samples Per Frame % Number of samples per frame, specified as an even positive @@ -41,8 +49,16 @@ '', 'SamplesPerFrame'); obj.SamplesPerFrame = value; end + % Dependent + function value = get.SamplingRate(obj) + if obj.ConnectedToDevice + value = double(obj.getAttributeLongLong('voltage0_i','sampling_frequency',obj.isOutput)); + else + value = NaN; + end + end %% Helpers - function [num_coarse, num_fine, num_data] = GetDataPathConfiguration(obj, isTx) + function [num_coarse, num_fine, num_data, sr] = GetDataPathConfiguration(obj, isTx) if nargin < 2 isTx = isa(obj,'adi.AD9081.Tx'); end @@ -74,6 +90,7 @@ end %% Parse data path configuration + sr = obj.SamplingRate; numChannels = obj.iio_device_get_channels_count(dev); map = {}; paths = {}; diff --git a/+adi/+AD9081/Rx.m b/+adi/+AD9081/Rx.m index 03727e12..e9450576 100644 --- a/+adi/+AD9081/Rx.m +++ b/+adi/+AD9081/Rx.m @@ -9,15 +9,7 @@ % AD9081 Datasheet % % See also adi.DAQ2.Rx - - properties (Dependent) - %SamplingRate Sampling Rate - % Baseband sampling rate in Hz, specified as a scalar - % in samples per second. This value is only readable once - % connected to hardware - SamplingRate - end - + properties %ChannelNCOFrequencies Channel NCO Frequencies % Frequency of NCO in fine decimators in receive path. Property @@ -116,14 +108,6 @@ obj.MainNCOPhases = zeros(1,obj.num_coarse_attr_channels); end - function value = get.SamplingRate(obj) - if obj.ConnectedToDevice - value= obj.getAttributeLongLong('voltage0_i','sampling_frequency',false); - else - value = NaN; - end - end - % Check ChannelNCOFrequencies function set.ChannelNCOFrequencies(obj, value) obj.CheckAndUpdateHW(value,'ChannelNCOFrequencies',... diff --git a/+adi/+AD9081/Tx.m b/+adi/+AD9081/Tx.m index b6a5dbd3..0da57e95 100644 --- a/+adi/+AD9081/Tx.m +++ b/+adi/+AD9081/Tx.m @@ -33,7 +33,7 @@ % Frequency of NCO in fine decimators in transmit path. Property % must be a [1,N] vector where each value is the frequency of an % NCO in hertz. - ChannelNCOGainScales = [0,0,0,0]; + ChannelNCOGainScales = [0.5,0.5,0.5,0.5]; %NCOEnables NCO Enables % Vector of logicals which enabled individual NCOs in channel % interpolators @@ -96,8 +96,8 @@ obj.MainNCOFrequencies = zeros(1,obj.num_coarse_attr_channels); obj.ChannelNCOPhases = zeros(1,obj.num_fine_attr_channels); obj.MainNCOPhases = zeros(1,obj.num_coarse_attr_channels); - obj.ChannelNCOGainScales = zeros(1,obj.num_fine_attr_channels); - obj.NCOEnables = zeros(1,obj.num_fine_attr_channels) > 0; + obj.ChannelNCOGainScales = 0.5.*ones(1,obj.num_fine_attr_channels); + obj.NCOEnables = ones(1,obj.num_fine_attr_channels) > 0; end % Check ChannelNCOFrequencies function set.ChannelNCOFrequencies(obj, value) diff --git a/+adi/+common b/+adi/+common index 385bb5f6..847122c6 160000 --- a/+adi/+common +++ b/+adi/+common @@ -1 +1 @@ -Subproject commit 385bb5f65fd41e0e37405dff45e8a3a0461634da +Subproject commit 847122c65828df2849236c34ec10375c4dab3689 diff --git a/JenkinsfileHW b/JenkinsfileHW index b504e215..c403b701 100644 --- a/JenkinsfileHW +++ b/JenkinsfileHW @@ -44,9 +44,14 @@ lock(label: 'adgt_test_harness_boards') { harness.set_lock_agent(true) // Required for MATLAB toolbox tests harness.set_elastic_server('192.168.10.1') harness.set_required_hardware(["zynq-zc706-adv7511-fmcdaq2", - "zynqmp-zcu102-rev10-fmcdaq3", - "zynqmp-zcu102-rev10-ad9081-vm8-l4", - "zynqmp-zcu102-rev10-ad9081-vm4-l8"]) + "zynqmp-zcu102-rev10-fmcdaq3", + "zynqmp-zcu102-rev10-ad9081-vm8-l4", + "zynqmp-zcu102-rev10-ad9081-vm4-l8", + "zynqmp-zcu102-rev10-ad9081-v204b-txmode9-rxmode4", + "zynqmp-zcu102-rev10-ad9081-v204c-txmode0-rxmode1", + "zynq-zc706-adv7511-fmcomms11", + "zynqmp-zcu102-rev10-ad9172-fmc-ebz-mode4"]) + harness.set_docker_args(['Vivado', 'MATLAB']) harness.set_nebula_local_fs_source_root("artifactory.analog.com") @@ -59,6 +64,13 @@ lock(label: 'adgt_test_harness_boards') { "pyenv('Version','/usr/bin/python3')", "runHWTests(getenv('board'))"]) harness.add_stage(harness.stage_library("MATLABTests"),'continueWhenFail') + def saveFigures = { + stage('Save Figure') { + archiveArtifacts artifacts: '*.png', followSymlinks: false, allowEmptyArchive: true + archiveArtifacts artifacts: '*.fig', followSymlinks: false, allowEmptyArchive: true + } + } + harness.add_stage(saveFigures,'continueWhenFail') harness.add_stage(harness.stage_library('SendResults'),'continueWhenFail') diff --git a/test/AD9081HWTests.m b/test/AD9081HWTests.m index 67c1a9bb..aa75b137 100644 --- a/test/AD9081HWTests.m +++ b/test/AD9081HWTests.m @@ -1,34 +1,62 @@ classdef AD9081HWTests < HardwareTests properties - uri = 'ip:analog'; + uri = 'ip:analog.local'; author = 'ADI'; end methods(TestClassSetup) % Check hardware connected function CheckForHardware(testCase) - Device = @()adi.AD9081.Rx; - testCase.CheckDevice('ip',Device,testCase.uri(4:end),false); + disp('Skipping init test'); +% Device = @()adi.AD9081.Rx; +% testCase.CheckDevice('ip',Device,testCase.uri(4:end),false); end end methods (Static) - function estFrequency(data,fs) + function estFrequency(data,fs,saveNoShow,figname) nSamp = length(data); FFTRxData = fftshift(10*log10(abs(fft(data)))); % df = fs/nSamp; freqRangeRx = (-fs/2:df:fs/2-df).'/1000; % plot(freqRangeRx, FFTRxData); df = fs/nSamp; freqRangeRx = (0:df:fs/2-df).'/1000; + if nargin < 3 + saveNoShow = false; + end + if nargin < 4 + figname = 'freq_plot'; + end + if saveNoShow + f = figure('visible','off'); + end plot(freqRangeRx, FFTRxData(end-length(freqRangeRx)+1:end,:)); + if saveNoShow + saveas(f,figname,'png') + saveas(f,figname,'fig') + end end - function freq = estFrequencyMax(data,fs) + function freq = estFrequencyMax(data,fs,saveNoShow,figname) nSamp = length(data); FFTRxData = fftshift(10*log10(abs(fft(data)))); df = fs/nSamp; freqRangeRx = (0:df:fs/2-df).'; [~,ind] = max(FFTRxData(end-length(freqRangeRx)+1:end,:)); freq = freqRangeRx(ind); + if nargin < 3 + saveNoShow = false; + end + if nargin < 4 + figname = 'freq_plot'; + end + if saveNoShow + f = figure('visible','off'); + end + plot(freqRangeRx, FFTRxData(end-length(freqRangeRx)+1:end,:)); + if saveNoShow + saveas(f,figname,'png') + saveas(f,figname,'fig') + end end end @@ -38,6 +66,13 @@ function estFrequency(data,fs) function testAD9081Rx(testCase) % Test Rx DMA data output rx = adi.AD9081.Rx('uri',testCase.uri); + [cdc, fdc, dc] = rx.GetDataPathConfiguration(); + testCase.log(sprintf('cdc: %d, fdc: %d, dc: %d',cdc, fdc, dc)) + rx = adi.AD9081.Rx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc); rx.EnabledChannels = 1; [out, valid] = rx(); rx.release(); @@ -48,22 +83,41 @@ function testAD9081Rx(testCase) function testAD9081RxWithTxDDS(testCase) % Test DDS output tx = adi.AD9081.Tx('uri',testCase.uri); + [cdc, fdc, dc] = tx.GetDataPathConfiguration(); + testCase.log(sprintf('cdc: %d, fdc: %d, dc: %d',cdc, fdc, dc)) + tx = adi.AD9081.Tx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc, ... + 'num_dds_channels', fdc*2); tx.DataSource = 'DDS'; toneFreq = 45e6; - tx.DDSFrequencies = repmat(toneFreq,2,2); +% tx.DDSFrequencies = repmat(toneFreq,2,2); +% tx.DDSScales = repmat(0.9,2,2); + tx.DDSSingleTone(toneFreq, 0.1, 1); +% tx.NCOEnables(:) = 1; tx(); pause(1); rx = adi.AD9081.Rx('uri',testCase.uri); + [cdc, fdc, dc] = rx.GetDataPathConfiguration(); + testCase.log(sprintf('cdc: %d, fdc: %d, dc: %d',cdc, fdc, dc)) + rx = adi.AD9081.Rx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc); rx.EnabledChannels = 1; valid = false; for k=1:10 [out, valid] = rx(); end + sr = rx.SamplingRate; rx.release(); % plot(real(out)); % testCase.estFrequency(out,rx.SamplingRate); - freqEst = meanfreq(double(real(out)),rx.SamplingRate); + freqEst = meanfreq(double(real(out)),sr); testCase.verifyTrue(valid); testCase.verifyGreaterThan(sum(abs(double(out))),0); @@ -74,25 +128,45 @@ function testAD9081RxWithTxDDS(testCase) function testAD9081RxWithTxDDSTwoChan(testCase) % Test DDS output tx = adi.AD9081.Tx('uri',testCase.uri); + [cdc, fdc, dc, sr] = tx.GetDataPathConfiguration(); + tx = adi.AD9081.Tx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc, ... + 'num_dds_channels', fdc*2); tx.DataSource = 'DDS'; - toneFreq1 = 160e6; - toneFreq2 = 300e6; - tx.DDSFrequencies = [toneFreq1,toneFreq2;toneFreq1,toneFreq2]; - tx.DDSScales = [1,1;0,0].*0.029; + toneFreq1 = sr/4; + toneFreq2 = sr/5; +% tx.DDSFrequencies = [toneFreq1,toneFreq1,toneFreq2,toneFreq2;... +% 0,0,0,0]; +% tx.DDSScales = [1,1,1,1;0,0,0,0].*0.029; + tx.DDSPhases = [90000,0,90000,0; 0,0,0,0]; + tx.DDSFrequencies = repmat(horzcat([toneFreq1,toneFreq1], ... + [toneFreq2,toneFreq2]),2,1); + tx.DDSScales = repmat([1,1;0,0].*0.029,1,2); tx(); pause(1); rx = adi.AD9081.Rx('uri',testCase.uri); + [cdc, fdc, dc] = rx.GetDataPathConfiguration(); + testCase.log(sprintf('cdc: %d, fdc: %d, dc: %d',cdc, fdc, dc)) + rx = adi.AD9081.Rx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc); rx.EnabledChannels = [1 2]; valid = false; for k=1:10 [out, valid] = rx(); end + sr = rx.SamplingRate; rx.release(); % plot(real(out)); -% testCase.estFrequency(out,rx.SamplingRate); - freqEst1 = testCase.estFrequencyMax(out(:,1),rx.SamplingRate); - freqEst2 = testCase.estFrequencyMax(out(:,2),rx.SamplingRate); +% testCase.estFrequency(out,sr); + freqEst1 = testCase.estFrequencyMax(out(:,1),sr,true,'TwoChanDDS_Chan1'); + freqEst2 = testCase.estFrequencyMax(out(:,2),sr,true,'TwoChanDDS_Chan2'); % freqEst1 = meanfreq(double(real(out(:,1))),rx.SamplingRate); % freqEst2 = meanfreq(double(real(out(:,2))),rx.SamplingRate); @@ -105,27 +179,45 @@ function testAD9081RxWithTxDDSTwoChan(testCase) end function testAD9081RxWithTxData(testCase) + + tx = adi.AD9081.Tx('uri',testCase.uri); + [cdc, fdc, dc, sr] = tx.GetDataPathConfiguration(); + % Test Tx DMA data output - amplitude = 2^15; frequency = 40e6; + amplitude = 2^15; frequency = sr/6; swv1 = dsp.SineWave(amplitude, frequency); swv1.ComplexOutput = false; swv1.SamplesPerFrame = 2^20; - swv1.SampleRate = 1e9; + swv1.SampleRate = sr; y = swv1(); - tx = adi.AD9081.Tx('uri',testCase.uri); + tx = adi.AD9081.Tx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc, ... + 'num_dds_channels', fdc*2); tx.DataSource = 'DMA'; tx.EnableCyclicBuffers = true; tx(y); rx = adi.AD9081.Rx('uri',testCase.uri); + [cdc, fdc, dc] = rx.GetDataPathConfiguration(); + testCase.log(sprintf('cdc: %d, fdc: %d, dc: %d',cdc, fdc, dc)) + rx = adi.AD9081.Rx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc); + rx.EnabledChannels = 1; for k=1:10 [out, valid] = rx(); end + sr = rx.SamplingRate; rx.release(); % plot(real(out)); - freqEst = meanfreq(double(real(out)),rx.SamplingRate); + freqEst = meanfreq(double(real(out)),sr); testCase.verifyTrue(valid); testCase.verifyGreaterThan(sum(abs(double(out))),0); @@ -134,37 +226,54 @@ function testAD9081RxWithTxData(testCase) end function testAD9081RxWithTxDataTwoChan(testCase) + + tx = adi.AD9081.Tx('uri',testCase.uri); + [cdc, fdc, dc, sr] = tx.GetDataPathConfiguration(); + % Test Tx DMA data output - amplitude = 2^15; toneFreq1 = 40e6; + amplitude = 2^15; toneFreq1 = sr/5; swv1 = dsp.SineWave(amplitude, toneFreq1); swv1.ComplexOutput = false; swv1.SamplesPerFrame = 2^20; - swv1.SampleRate = 1e9; + swv1.SampleRate = sr; y1 = swv1(); - amplitude = 2^15; toneFreq2 = 180e6; + amplitude = 2^15; toneFreq2 = sr/8; swv1 = dsp.SineWave(amplitude, toneFreq2); swv1.ComplexOutput = false; swv1.SamplesPerFrame = 2^20; - swv1.SampleRate = 1e9; + swv1.SampleRate = sr; y2 = swv1(); - tx = adi.AD9081.Tx('uri',testCase.uri); + tx = adi.AD9081.Tx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc, ... + 'num_dds_channels', fdc*2); tx.DataSource = 'DMA'; tx.EnableCyclicBuffers = true; tx.EnabledChannels = [1,2]; tx([y1,y2]); rx = adi.AD9081.Rx('uri',testCase.uri); + [cdc, fdc, dc] = rx.GetDataPathConfiguration(); + testCase.log(sprintf('cdc: %d, fdc: %d, dc: %d',cdc, fdc, dc)) + rx = adi.AD9081.Rx(... + 'uri',testCase.uri,... + 'num_data_channels', dc, ... + 'num_coarse_attr_channels', cdc, ... + 'num_fine_attr_channels', fdc); rx.EnabledChannels = [1,2]; for k=1:10 [out, valid] = rx(); end + sr = rx.SamplingRate; rx.release(); - plot(real(out)); +% plot(real(out)); % testCase.estFrequency(out,rx.SamplingRate); - freqEst1 = testCase.estFrequencyMax(out(:,1),rx.SamplingRate); - freqEst2 = testCase.estFrequencyMax(out(:,2),rx.SamplingRate); + freqEst1 = testCase.estFrequencyMax(out(:,1),sr,true,'TwoChanData_Chan1'); + freqEst2 = testCase.estFrequencyMax(out(:,2),sr,true,'TwoChanData_Chan2'); % freqEst = meanfreq(double(real(out)),rx.SamplingRate); testCase.verifyTrue(valid); diff --git a/test/FMCOMMS11Test.m b/test/FMCOMMS11Test.m index 2a517093..7f231a2f 100644 --- a/test/FMCOMMS11Test.m +++ b/test/FMCOMMS11Test.m @@ -1,9 +1,36 @@ -classdef FMCOMMS11Test < matlab.unittest.TestCase +classdef FMCOMMS11Test < HardwareTests properties uri ='ip:analog.local'; author = 'ADI'; end + methods(TestClassSetup) + % Check hardware connected + function CheckForHardware(testCase) + Device = @()adi.FMCOMMS11.Rx; + testCase.CheckDevice('ip',Device,testCase.uri(4:end),false); + end + end + + methods (Static) + function estFrequency(data,fs) + nSamp = length(data); + FFTRxData = fftshift(10*log10(abs(fft(data)))); +% df = fs/nSamp; freqRangeRx = (-fs/2:df:fs/2-df).'/1000; +% plot(freqRangeRx, FFTRxData); + df = fs/nSamp; freqRangeRx = (0:df:fs/2-df).'/1000; + plot(freqRangeRx, FFTRxData(end-length(freqRangeRx)+1:end,:)); + end + + function freq = estFrequencyMax(data,fs) + nSamp = length(data); + FFTRxData = fftshift(10*log10(abs(fft(data)))); + df = fs/nSamp; freqRangeRx = (0:df:fs/2-df).'; + [~,ind] = max(FFTRxData(end-length(freqRangeRx)+1:end,:)); + freq = freqRangeRx(ind); + end + + end methods(Test) function testFMCOMMS11Rx(testCase) @@ -31,12 +58,13 @@ function testFMCOMMS11RxWithTxDDS(testCase) for k=1:10 [out, valid] = rx(); end - freqEst = meanfreq(double(real(out)),rx.SamplingRate); +% freqEst = meanfreq(double(real(out)),rx.SamplingRate); + freqEst = testCase.estFrequencyMax(out(:,1),rx.SamplingRate); rx.release(); testCase.verifyTrue(valid); testCase.verifyGreaterThan(sum(abs(double(out))),0); - testCase.verifyEqual(freqEst,toneFreq,'RelTol',0.01,... + testCase.verifyEqual(double(freqEst),toneFreq,'RelTol',0.01,... 'Frequency of DDS tone unexpected') end end diff --git a/test/runHWTests.m b/test/runHWTests.m index 5e076abd..cdcd6b35 100644 --- a/test/runHWTests.m +++ b/test/runHWTests.m @@ -24,11 +24,15 @@ function runHWTests(board) "zynqmp-zcu102-rev10-ad9081-vm4-l8", ... "zynqmp-zcu102-rev10-ad9081-vm8-l4"} at = 'AD9081'; + case {"zynq-zc706-adv7511-fmcomms11"} + at = 'FMCOMMS11'; + case {"zynqmp-zcu102-rev10-ad9172-fmc-ebz-mode4"} + at = 'AD9172'; otherwise error('%s unsupported for HW test harness', board); end - ats = {'DAQ2Tests','DAQ3Tests','AD9081HWTests'}; + ats = {'DAQ2Tests','DAQ3Tests','AD9081HWTests','FMCOMMS11Test'}; if nargin == 0 suite = testsuite(ats); @@ -62,6 +66,6 @@ function runHWTests(board) exit(1); end save(['BSPTest_',datestr(now,'dd_mm_yyyy-HH_MM_SS'),'.mat'],'t'); -bdclose('all'); -exit(any([results.Failed])); + bdclose('all'); + exit(any([results.Failed])); end