diff --git a/LICENSE.md b/LICENSE similarity index 73% rename from LICENSE.md rename to LICENSE index 28efaf56..fae99263 100644 --- a/LICENSE.md +++ b/LICENSE @@ -1,10 +1,6 @@ MIT License -Copyright (c) 2017 Tobias Knopp - -Copyright for some IP cores and scripts of project RedPitayaDAQServer are held by Pavel Demin 2014-present -as part of project red-pitaya-notes (https://github.com/pavel-demin/red-pitaya-notes). -Other copyright for some IP cores are held by Koheron, 2015-present as a part of their SDK (https://github.com/Koheron/koheron-sdk). +Copyright (c) 2017 Tobias Knopp and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index f3b5e742..6d708e03 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # RedPitayaDAQServer -This project contains software to be used with the STEMlab 125-14 device (including the Zynq 7020-variant) from RedPitaya. It allows for continuous generation and measurement of signals with up to 15.625 MS/s, which is not possible with the standard image of the RedPitaya. In addition, the software allows to synchronize a cluster of multiple RedPitayas. - -## Documentation - -Please read the docs [![](https://img.shields.io/badge/docs-latest-blue.svg)](https://tknopp.github.io/RedPitayaDAQServer/dev) for further information on building, usage and specifications. - +This project contains software to be used with the STEMlab 125-14 device (including the Zynq 7020-variant) from [Red Pitaya](https://redpitaya.com/). It allows for continuous generation and measurement of signals with up to 15.625 MS/s, which is not possible with the standard image of the Red Pitaya. In addition, the software allows to synchronize a cluster of multiple Red Pitayas. +[![Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://tknopp.github.io/RedPitayaDAQServer/stable) +[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://tknopp.github.io/RedPitayaDAQServer/dev) +[![Build status](https://github.com/tknopp/RedPitayaDAQServer/workflows/CI/badge.svg)](https://github.com/tknopp/RedPitayaDAQServer/actions) +[![License](https://img.shields.io/github/license/tknopp/RedPitayaDAQServer?color=green&style=flat)](https://github.com/tknopp/RedPitayaDAQServer/blob/main/LICENSE) +[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl) diff --git a/src/client/julia/.JuliaFormatter.toml b/src/client/julia/.JuliaFormatter.toml new file mode 100644 index 00000000..3bf1a254 --- /dev/null +++ b/src/client/julia/.JuliaFormatter.toml @@ -0,0 +1,15 @@ +indent = 2 +margin = 110 +whitespace_typedefs = true +whitespace_ops_in_indices = true +remove_extra_newlines = true +always_use_return = true +whitespace_in_kwargs = true +format_docstrings = true +conditional_to_if = true +separate_kwargs_with_semicolon = true +always_for_in = true +for_in_replacement = "∈" + +short_to_long_function_def = true +long_to_short_function_def = true \ No newline at end of file diff --git a/src/client/julia/.gitignore b/src/client/julia/.gitignore index 5f7f8eb3..cc236768 100644 --- a/src/client/julia/.gitignore +++ b/src/client/julia/.gitignore @@ -5,3 +5,5 @@ docs/build/ Manifest.toml + +test/config.jl \ No newline at end of file diff --git a/src/client/julia/Project.toml b/src/client/julia/Project.toml index c64d5467..cb0a62b7 100644 --- a/src/client/julia/Project.toml +++ b/src/client/julia/Project.toml @@ -4,6 +4,7 @@ authors = ["Tobias Knopp "] version = "0.8.0" [deps] +DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" @@ -11,17 +12,20 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] Aqua = "0.8" +DocStringExtensions = "0.8, 0.9" +LinearAlgebra = "1" +Random = "1" Statistics = "1" Sockets = "1" -LinearAlgebra = "1" Test = "1" -Random = "1" julia = "1.7" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "Test", "Random"] +test = ["Aqua", "JuliaFormatter", "Random", "Sockets", "Test"] diff --git a/src/client/julia/src/ADC.jl b/src/client/julia/src/ADC.jl index 4ac7d1e6..e270874e 100644 --- a/src/client/julia/src/ADC.jl +++ b/src/client/julia/src/ADC.jl @@ -1,12 +1,38 @@ -export TriggerMode, INTERNAL, EXTERNAL, ADCPerformanceData, RPStatus, PerformanceData, RPPerformance, RPInfo, -decimation, decimation!, numChan, samplesPerPeriod, samplesPerPeriod!, periodsPerFrame, periodsPerFrame!, -currentWP, currentPeriod, currentFrame, masterTrigger, masterTrigger!, keepAliveReset, keepAliveReset!, -triggerMode, triggerMode!, overwritten, corrupted, serverStatus, performanceData, -readSamples, startPipelinedData, stopTransmission, triggerPropagation, triggerPropagation! +export TriggerMode, + INTERNAL, + EXTERNAL, + ADCPerformanceData, + RPStatus, + PerformanceData, + RPPerformance, + RPInfo, + decimation, + decimation!, + numChan, + samplesPerPeriod, + samplesPerPeriod!, + periodsPerFrame, + periodsPerFrame!, + currentWP, + currentPeriod, + currentFrame, + masterTrigger, + masterTrigger!, + keepAliveReset, + keepAliveReset!, + triggerMode, + triggerMode!, + overwritten, + corrupted, + serverStatus, + performanceData, + readSamples, + startPipelinedData, + stopTransmission, + triggerPropagation, + triggerPropagation! """ - TriggerMode - Represent the different trigger modes the FPGA image can have. Valid value are `INTERNAL` and `EXTERNAL`. See [`triggerMode`](@ref), [`triggerMode!`](@ref). @@ -27,11 +53,17 @@ struct RPStatus dacEnabled::Bool end -RPStatus(statusRaw::Integer) = RPStatus((statusRaw >> 0) & 1, (statusRaw >> 1) & 1, (statusRaw >> 2) & 1, (statusRaw >> 3) & 1, (statusRaw >> 4) & 1) +function RPStatus(statusRaw::Integer) + return RPStatus( + (statusRaw >> 0) & 1, + (statusRaw >> 1) & 1, + (statusRaw >> 2) & 1, + (statusRaw >> 3) & 1, + (statusRaw >> 4) & 1, + ) +end """ - PerformanceData - Holds the performance data that is used for monitoring. """ struct PerformanceData @@ -49,28 +81,24 @@ struct RPInfo performances::Vector{RPPerformance} end -function RPInfo() - return RPInfo([RPPerformance([])]) -end +RPInfo() = RPInfo([RPPerformance([])]) -function dataRate(chunkSize, deltaSend, decimation; unit="Mbits") +function dataRate(chunkSize, deltaSend, decimation; unit = "Mbits") bitsPerSample = (chunkSize * 4 * 8) / deltaSend # 4 bytes per samples freq = div(125e6, decimation) - bitsPerSec = bitsPerSample * freq - return bitsPerSec/1e6 #TODO unit conversion + bitsPerSec = bitsPerSample * freq + return bitsPerSec / 1e6 #TODO unit conversion end - -function dataRate(adc::ADCPerformanceData, decimation; unit ="Mbits") +function dataRate(adc::ADCPerformanceData, decimation; unit = "Mbits") return dataRate(adc.chunkSize, adc.deltaSend, decimation) end """ - decimation(rp::RedPitaya) - Return the decimation of the RedPitaya. # Examples + ```julia julia> decimation!(rp, 8) true @@ -83,11 +111,10 @@ decimation(rp::RedPitaya) = query(rp, scpiCommand(decimation), scpiReturn(decima scpiCommand(::typeof(decimation)) = "RP:ADC:DECimation?" scpiReturn(::typeof(decimation)) = Int64 """ - decimation!(rp::RedPitaya, dec) - Set the decimation of the RedPitaya. Return `true` if the command was successful. # Examples + ```julia julia> decimation!(rp, 8) true @@ -103,43 +130,35 @@ end scpiCommand(::typeof(decimation!), dec) = string("RP:ADC:DECimation ", dec) scpiReturn(::typeof(decimation!)) = Bool """ - numChan(rp::RedPitaya) - Return the number of ADC channel of a RedPitaya. """ numChan(rp::RedPitaya) = 2 """ - samplesPerPeriod(rp::RedPitaya) - Return the number of samples per period. # Example + ```julia julia> samplesPerPeriod!(rp, 256) true julia> samplesPerPeriod(rp) 256 - ``` """ -function samplesPerPeriod(rp::RedPitaya) - return rp.samplesPerPeriod -end +samplesPerPeriod(rp::RedPitaya) = rp.samplesPerPeriod """ - samplesPerPeriod!(rp::RedPitaya, value) - Set the number of samples per period. # Example + ```julia julia> samplesPerPeriod!(rp, 256) true julia> samplesPerPeriod(rp) 256 - ``` """ function samplesPerPeriod!(rp::RedPitaya, value) @@ -148,34 +167,28 @@ function samplesPerPeriod!(rp::RedPitaya, value) end """ - periodsPerFrame(rp::RedPitaya) - Return the number of periods per frame. # Example + ```julia julia> periodsPerFrame!(rp, 16) julia> periodsPerFrame(rp) 16 - ``` """ -function periodsPerFrame(rp::RedPitaya) - return rp.periodsPerFrame -end +periodsPerFrame(rp::RedPitaya) = rp.periodsPerFrame """ - periodsPerFrame(rp::RedPitaya, value) - Set the number of periods per frame. # Example + ```julia julia> periodsPerFrame!(rp, 16) julia> periodsPerFrame(rp) 16 - ``` """ function periodsPerFrame!(rp::RedPitaya, value) @@ -184,46 +197,35 @@ function periodsPerFrame!(rp::RedPitaya, value) end """ - currentFrame(rp::RedPitaya) - Return the current frame of the RedPitaya based on the current writepointer, samples per period and periods per frame. See also [`currentWP`](@ref), [`samplesPerPeriod`](@ref), [`periodsPerFrame`](@ref). """ -function currentFrame(rp::RedPitaya) - return Int64(floor(currentWP(rp) / (rp.samplesPerPeriod * rp.periodsPerFrame))) -end +currentFrame(rp::RedPitaya) = Int64(floor(currentWP(rp) / (rp.samplesPerPeriod * rp.periodsPerFrame))) """ - currentPeriod(rp::RedPitaya) - Return the current period of the RedPitaya based on the current writepointer and samples per period. See also [`currentWP`](@ref), [`samplesPerPeriod`](@ref). """ -function currentPeriod(rp::RedPitaya) - return Int64(floor(currentWP(rp) / (rp.samplesPerPeriod))) -end +currentPeriod(rp::RedPitaya) = Int64(floor(currentWP(rp) / (rp.samplesPerPeriod))) """ - currentWP(rp::RedPitaya) - Return the current writepointer of the RedPitaya. """ currentWP(rp::RedPitaya) = query(rp, scpiCommand(currentWP), scpiReturn(currentWP)) scpiCommand(::typeof(currentWP)) = "RP:ADC:WP?" scpiReturn(::typeof(currentWP)) = Int64 -bufferSize(rp::RedPitaya) = query(rp,scpiCommand(bufferSize), scpiReturn(bufferSize)) +bufferSize(rp::RedPitaya) = query(rp, scpiCommand(bufferSize), scpiReturn(bufferSize)) scpiCommand(::typeof(bufferSize)) = "RP:ADC:BUFFER:SIZE?" scpiReturn(::typeof(bufferSize)) = Int64 """ - masterTrigger!(rp::RedPitaya, val::Bool) - Set the master trigger of the RedPitaya to `val`. Return `true` if the command was successful. # Example + ```julia julia> masterTrigger!(rp, true) true @@ -232,17 +234,15 @@ julia>masterTrigger(rp) true ``` """ -function masterTrigger!(rp::RedPitaya, val) - return query(rp, scpiCommand(masterTrigger!, val), scpiReturn(masterTrigger!)) -end +masterTrigger!(rp::RedPitaya, val) = query(rp, scpiCommand(masterTrigger!, val), scpiReturn(masterTrigger!)) scpiCommand(::typeof(masterTrigger!), val::Bool) = scpiCommand(masterTrigger!, val ? "ON" : "OFF") scpiCommand(::typeof(masterTrigger!), val::String) = string("RP:TRIGger ", val) scpiReturn(::typeof(masterTrigger!)) = Bool """ - masterTrigger(rp::RedPitaya) - Determine whether the master trigger is set. + # Example + ```julia julia> masterTrigger!(rp, true) @@ -256,8 +256,6 @@ scpiReturn(::typeof(masterTrigger)) = String parseReturn(::typeof(masterTrigger), ret) = occursin("ON", ret) """ - keepAliveReset!(rp::RedPitaya, val::Bool) - Set the keepAliveReset to `val`. """ function keepAliveReset!(rp::RedPitaya, val::Bool) @@ -267,8 +265,6 @@ scpiCommand(::typeof(keepAliveReset!), val::Bool) = scpiCommand(keepAliveReset!, scpiCommand(::typeof(keepAliveReset!), val::String) = string("RP:TRIGger:ALiVe ", val) scpiReturn(::typeof(keepAliveReset!)) = Bool """ - keepAliveReset(rp::RedPitaya) - Determine whether the keepAliveReset is set. """ keepAliveReset(rp::RedPitaya) = occursin("ON", query(rp, scpiCommand(keepAliveReset))) @@ -276,19 +272,12 @@ scpiCommand(::typeof(keepAliveReset)) = "RP:TRIGger?" scpiReturn(::typeof(keepAliveReset)) = String parseReturn(::typeof(keepAliveReset), ret) = occursin("ON", ret) - # "INTERNAL" or "EXTERNAL" """ - triggerMode!(rp::RedPitaya, mode::String) - Set the trigger mode of the RedPitaya. Return `true` if the command was successful. """ -function triggerMode!(rp::RedPitaya, mode::String) - return triggerMode!(rp, stringToEnum(TriggerMode, mode)) -end +triggerMode!(rp::RedPitaya, mode::String) = triggerMode!(rp, stringToEnum(TriggerMode, mode)) """ - triggerMode!(rp::RedPitaya, mode::String) - Set the trigger mode of the RedPitaya. Return `true` if the command was successful. """ function triggerMode!(rp::RedPitaya, mode::TriggerMode) @@ -297,19 +286,16 @@ end scpiCommand(::typeof(triggerMode!), mode) = string("RP:TRIGger:MODe ", string(mode)) scpiReturn(::typeof(triggerMode!)) = Bool -function triggerMode(rp::RedPitaya) - return stringToEnum(TriggerMode, strip(query(rp, "RP:TRIGger:MODe?"), '\"')) -end +triggerMode(rp::RedPitaya) = stringToEnum(TriggerMode, strip(query(rp, "RP:TRIGger:MODe?"), '\"')) scpiCommand(::typeof(triggerMode)) = "RP:TRIGger:MODe?" scpiReturn(::typeof(triggerMode)) = TriggerMode parseReturn(::typeof(triggerMode), ret) = stringToEnum(TriggerMode, strip(ret, '\"')) """ - triggerPropagation!(rp::RedPitaya, val::Bool) - Set the trigger propagation of the RedPitaya to `val`. Return `true` if the command was successful. # Example + ```julia julia> triggerPropagation!(rp, true) true @@ -325,10 +311,10 @@ scpiCommand(::typeof(triggerPropagation!), val::Bool) = scpiCommand(triggerPropa scpiCommand(::typeof(triggerPropagation!), val::String) = string("RP:TRIGger:PROP ", val) scpiReturn(::typeof(triggerPropagation!)) = Bool """ - triggerPropagation(rp::RedPitaya) - Determine whether the trigger propagation is set. + # Example + ```julia julia> triggerPropagation!(rp, true) @@ -341,7 +327,6 @@ scpiCommand(::typeof(triggerPropagation)) = "RP:TRIGger:PROP?" scpiReturn(::typeof(triggerPropagation)) = String parseReturn(::typeof(triggerPropagation), ret) = occursin("ON", ret) - overwritten(rp::RedPitaya) = query(rp, scpiCommand(overwritten), scpiReturn(overwritten)) scpiCommand(::typeof(overwritten)) = "RP:STATus:OVERwritten?" scpiReturn(::typeof(overwritten)) = Bool @@ -349,12 +334,11 @@ corrupted(rp::RedPitaya) = query(rp, scpiCommand(corrupted), scpiReturn(corrupte scpiCommand(::typeof(corrupted)) = "RP:STATus:CORRupted?" scpiReturn(::typeof(corrupted)) = Bool -serverStatus(rp::RedPitaya) = query(rp, scpiCommand(serverStatus), scpiReturn(serverStatus)) +serverStatus(rp::RedPitaya) = query(rp, scpiCommand(serverStatus), scpiReturn(serverStatus)) scpiCommand(::typeof(serverStatus)) = "RP:STATus?" scpiReturn(::typeof(serverStatus)) = RPStatus parseReturn(::typeof(serverStatus), ret) = RPStatus(parse(Int64, ret)) - function readServerStatus(rp::RedPitaya) statusRaw = read!(rp.dataSocket, Array{Int8}(undef, 1))[1] return RPStatus(statusRaw) @@ -382,9 +366,7 @@ function readChunkMetaData(rp::RedPitaya, reqWP, numSamples) return PerformanceData(UInt64(reqWP), adc, dac, status) end -function readSamples!(rp::RedPitaya, data::AbstractArray{Int16}) - return read!(rp.dataSocket, data) -end +readSamples!(rp::RedPitaya, data::AbstractArray{Int16}) = read!(rp.dataSocket, data) # Low level read, reads samples, error and perf. Values need to be already requested function readSamplesChunk_(rp::RedPitaya, reqWP::Int64, numSamples::Int64) @@ -402,14 +384,12 @@ function readSamplesChunk_(rp::RedPitaya, reqWP::Int64, buffer::AbstractArray{In end """ - startPipelinedData(rp::RedPitaya, reqWP, numSamples, chunkSize) - Instruct the `RedPitaya` to send `numSamples` samples from writepointer `reqWP` in chunks of `chunkSize`. """ function startPipelinedData(rp::RedPitaya, reqWP::Int64, numSamples::Int64, chunkSize::Int64) command = string("RP:ADC:DATA:PIPELINED? ", reqWP, ",", numSamples, ",", chunkSize) sending = query(rp, command, Bool) - + if !sending error("RedPitaya $(rp.host) can not start sample pipeline.") end diff --git a/src/client/julia/src/Acquisition.jl b/src/client/julia/src/Acquisition.jl index 9cb9265c..fb08c1ba 100644 --- a/src/client/julia/src/Acquisition.jl +++ b/src/client/julia/src/Acquisition.jl @@ -1,12 +1,18 @@ -export SampleChunk, startPipelinedData, readSamples, readFrames, readPeriods, convertSamplesToFrames, convertSamplesToFrames!, convertSamplesToPeriods! +export SampleChunk, + startPipelinedData, + readSamples, + readFrames, + readPeriods, + convertSamplesToFrames, + convertSamplesToFrames!, + convertSamplesToPeriods! """ - SampleChunk - Struct containing a matrix of samples and associated `PerformanceData` # Fields -- `samples::Matrix{Int16}`: `n`x`m` matrix containing `m` samples for `n` channel -- `performance::Vector{PerformanceData}`: `PerformanceData` object for each RedPitaya that transmitted samples + + - `samples::Matrix{Int16}`: `n`x`m` matrix containing `m` samples for `n` channel + - `performance::Vector{PerformanceData}`: `PerformanceData` object for each RedPitaya that transmitted samples """ struct SampleChunk samples::Matrix{Int16} @@ -14,54 +20,66 @@ struct SampleChunk end """ - startPipelinedData(rpu::Union{RedPitayaCluster, RedPitayaClusterView}, reqWP::Int64, numSamples::Int64, chunkSize::Int64) - Instruct all `RedPitaya`s to send `numSamples` samples from writepointer `reqWP` in chunks of `chunkSize`. See [`readSamples`](@ref) """ -function startPipelinedData(rpu::Union{RedPitayaCluster,RedPitayaClusterView}, reqWP::Int64, numSamples::Int64, chunkSize::Int64) - @sync for rp in rpu +function startPipelinedData( + rpu::Union{RedPitayaCluster, RedPitayaClusterView}, + reqWP::Int64, + numSamples::Int64, + chunkSize::Int64, +) + @sync for rp ∈ rpu @async startPipelinedData(rp, reqWP, numSamples, chunkSize) end end -function readSamplesHeartbeat(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, wpStart::Int64) +""" +Checks for the current writepointer if a timeout occured. +""" +function readSamplesHeartbeat(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, wpStart::Int64) heartbeatTimeout = 30.0 heartBeatStartTime = time() timeOutCounter = 1 while currentWP(rpu) < wpStart # Current WP query as a heartbeat to avoid timeouts with "distant" wpStarts timeDifference = time() - heartBeatStartTime - if timeDifference / (heartbeatTimeout*1000.0) >= timeOutCounter + if timeDifference / (heartbeatTimeout * 1000.0) >= timeOutCounter @warn "Still waiting for write pointer (currently it is $(currentWP(rpu)). Are you sure there is no error and this loop is running infinitely?" timeOutCounter += 1 end end end -correctFilterDelay(rpu::Union{RedPitaya, RedPitayaCluster}, wpStart) = correctFilterDelay(wpStart, decimation(rpu)) +function correctFilterDelay(rpu::Union{RedPitaya, RedPitayaCluster}, wpStart) + return correctFilterDelay(wpStart, decimation(rpu)) +end correctFilterDelay(rpcv::RedPitayaClusterView, wpStart) = correctFilterDelay(rpcv.rpc, wpStart) function correctFilterDelay(wpStart::Int64, dec::Int64) cic_stages = 6 fir_taps = 92 - cicDelay = (((dec/2-1)/2*cic_stages))/dec - firDelay = ((fir_taps-1)/2)/2 + cicDelay = (((dec / 2 - 1) / 2 * cic_stages)) / dec + firDelay = ((fir_taps - 1) / 2) / 2 delay = Int64(round((cicDelay + firDelay), RoundUp)) correctedWp = wpStart + delay @debug "Filter delay corrected $wpStart to $correctedWp ($cicDelay, $firDelay, $delay)" return correctedWp end - """ - readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, wpStart::Int64, numOfRequestedSamples::Int64; chunkSize::Int64 = 25000, rpInfo=nothing) - Request and receive `numOfRequestedSamples` samples from `wpStart` on in a pipelined fashion. Return a matrix of samples. If `rpInfo` is set to a `RPInfo`, the `PerformanceData` sent after every `chunkSize` samples will be pushed into `rpInfo`. """ -function readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, wpStart::Int64, numOfRequestedSamples::Int64; chunkSize::Int64 = 25000, rpInfo=nothing, correct_filter_delay::Bool = true) +function readSamples( + rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, + wpStart::Int64, + numOfRequestedSamples::Int64; + chunkSize::Int64 = 25000, + rpInfo = nothing, + correct_filter_delay::Bool = true, +) numOfReceivedSamples = 0 index = 1 rawData = zeros(Int16, numChan(rpu), numOfRequestedSamples) @@ -81,7 +99,7 @@ function readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView collectSamples!(rpu, wpRead, chunk, channel, chunkBuffer) numOfReceivedSamples += chunk temp = take!(channel) - + # Add Samples samples = temp.samples rawData[:, index:(index + chunk - 1)] = samples @@ -89,7 +107,7 @@ function readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView # Add Performance Info if !isnothing(rpInfo) perfs = temp.performance - for (i,perf) in enumerate(perfs) + for (i, perf) ∈ enumerate(perfs) push!(rpInfo.performances[i].data, perf) end end @@ -98,13 +116,18 @@ function readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView end """ - readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, wpStart::Int64, numOfRequestedSamples::Int64, channel::Channel; chunkSize::Int64 = 25000) - Request and receive `numOfRequestedSamples` samples from `wpStart` on in a pipelined fashion. The samples and associated `PerformanceData` are pushed into `channel` as a `SampleChunk`. See [`SampleChunk`](@ref). """ -function readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, wpStart::Int64, numOfRequestedSamples::Int64, channel::Channel; chunkSize::Int64 = 25000, correct_filter_delay::Bool = true) +function readSamples( + rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, + wpStart::Int64, + numOfRequestedSamples::Int64, + channel::Channel; + chunkSize::Int64 = 25000, + correct_filter_delay::Bool = true, +) numOfReceivedSamples = 0 chunkBuffer = zeros(Int16, chunkSize * 2, length(rpu)) @@ -121,24 +144,28 @@ function readSamples(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView collectSamples!(rpu, wpRead, chunk, channel, chunkBuffer) numOfReceivedSamples += chunk end - end -function collectSamples!(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, wpRead::Int64, chunk::Int64, channel::Channel, chunkBuffer) +function collectSamples!( + rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, + wpRead::Int64, + chunk::Int64, + channel::Channel, + chunkBuffer, +) done = zeros(Bool, length(rpu)) iterationDone = Condition() timeoutHappened = false result = zeros(Int16, numChan(rpu), chunk) performances = Vector{PerformanceData}(undef, length(rpu)) - - for (d, rp) in enumerate(rpu) + for (d, rp) ∈ enumerate(rpu) @async begin buffer = view(chunkBuffer, 1:(2 * chunk), d) (u, perf) = readSamplesChunk_(rp, Int64(wpRead), buffer) samples = reshape(u, 2, chunk) - result[2*d-1, :] = samples[1, :] - result[2*d, :] = samples[2, :] + result[2 * d - 1, :] = samples[1, :] + result[2 * d, :] = samples[2, :] performances[d] = perf done[d] = true @@ -163,33 +190,41 @@ function collectSamples!(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaCluster error("Timeout reached when reading from sockets") end - put!(channel, SampleChunk(result, performances)) - + return put!(channel, SampleChunk(result, performances)) end """ - readFrames(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, startFrame, numFrames, numBlockAverages=1, numPeriodsPerPatch=1; rpInfo=nothing, chunkSize = 50000, useCalibration = false) - Request and receive `numFrames` frames from `startFrame` on. See [`readSamples`](@ref), [`convertSamplesToFrames`](@ref), [`samplesPerPeriod`](@ref), [`periodsPerFrame`](@ref), [`updateCalib!`](@ref). # Arguments -- `rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}`: `RedPitaya`s to receive samples from. -- `startFrame`: frame from which to start transmitting -- `numFrames`: number of frames to read -- `numBlockAverages=1`: see `convertSamplesToFrames` -- `numPeriodsPerPatch=1`: see `convertSamplesToFrames` -- `chunkSize=50000`: see `readSamples` -- `rpInfo=nothing`: see `readSamples` -- `useCalibration`: convert from Int16 samples to Float32 values based on `RedPitaya`s calibration + + - `rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}`: `RedPitaya`s to receive samples from. + - `startFrame`: frame from which to start transmitting + - `numFrames`: number of frames to read + - `numBlockAverages=1`: see `convertSamplesToFrames` + - `numPeriodsPerPatch=1`: see `convertSamplesToFrames` + - `chunkSize=50000`: see `readSamples` + - `rpInfo=nothing`: see `readSamples` + - `useCalibration`: convert from Int16 samples to Float32 values based on `RedPitaya`s calibration """ -function readFrames(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, startFrame, numFrames, numBlockAverages=1, numPeriodsPerPatch=1; rpInfo=nothing, chunkSize = 50000, useCalibration = false, correct_filter_delay::Bool = true) +function readFrames( + rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, + startFrame, + numFrames, + numBlockAverages = 1, + numPeriodsPerPatch = 1; + rpInfo = nothing, + chunkSize = 50000, + useCalibration = false, + correct_filter_delay::Bool = true, +) numSampPerPeriod = samplesPerPeriod(rpu) numPeriods = periodsPerFrame(rpu) numSampPerFrame = numSampPerPeriod * numPeriods - if rem(numSampPerPeriod,numBlockAverages) != 0 + if rem(numSampPerPeriod, numBlockAverages) != 0 error("block averages has to be a divider of numSampPerPeriod") end @@ -197,30 +232,52 @@ function readFrames(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView} numOfRequestedSamples = numFrames * numSampPerFrame # rawSamples Int16 numofChan(rpc) x numOfRequestedSamples - rawSamples = readSamples(rpu, Int64(wpStart), Int64(numOfRequestedSamples), chunkSize = chunkSize, rpInfo = rpInfo, correct_filter_delay = correct_filter_delay) + rawSamples = readSamples( + rpu, + Int64(wpStart), + Int64(numOfRequestedSamples); + chunkSize = chunkSize, + rpInfo = rpInfo, + correct_filter_delay = correct_filter_delay, + ) # Reshape/Avg Data if useCalibration - data = convertSamplesToFrames(rpu, rawSamples, numChan(rpu), numSampPerPeriod, numPeriods, numFrames, numBlockAverages, numPeriodsPerPatch) + data = convertSamplesToFrames( + rpu, + rawSamples, + numChan(rpu), + numSampPerPeriod, + numPeriods, + numFrames, + numBlockAverages, + numPeriodsPerPatch, + ) else - data = convertSamplesToFrames(rawSamples, numChan(rpu), numSampPerPeriod, numPeriods, numFrames, numBlockAverages, numPeriodsPerPatch) + data = convertSamplesToFrames( + rawSamples, + numChan(rpu), + numSampPerPeriod, + numPeriods, + numFrames, + numBlockAverages, + numPeriodsPerPatch, + ) end return data end """ - convertSamplesToFrames(rpu::Union{RedPitayaCluster, RedPitayaClusterView}, samples, numChan, numSampPerPeriod, numPeriods, numFrames, numBlockAverages=1, numPeriodsPerPatch=1) - Converts a given set of samples to frames. See [`readFrames`](@ref) """ function convertSamplesToFrames(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, samples, numChan, numSampPerPeriod, numPeriods, numFrames, numBlockAverages=1, numPeriodsPerPatch=1) frames = convertSamplesToFrames(samples, numChan, numSampPerPeriod, numPeriods, numFrames, numBlockAverages, numPeriodsPerPatch) - calibs = [x.calib for x in rpu] + calibs = [x.calib for x ∈ rpu] calib = hcat(calibs...) - for d = 1:size(frames, 2) + for d ∈ 1:size(frames, 2) frames[:, d, :, :] .*= calib[1, d] frames[:, d, :, :] .+= calib[2, d] end @@ -228,8 +285,6 @@ function convertSamplesToFrames(rpu::Union{RedPitaya, RedPitayaCluster, RedPitay end """ - convertSamplesToFrames(samples, numChan, numSampPerPeriod, numPeriods, numFrames, numBlockAverages=1, numPeriodsPerPatch=1) - Converts a given set of samples to frames. See [`readFrames`](@ref) @@ -238,82 +293,111 @@ function convertSamplesToFrames(samples, numChan, numSampPerPeriod, numPeriods, if rem(numSampPerPeriod,numBlockAverages) != 0 error("block averages has to be a divider of numSampPerPeriod") end - numTrueSampPerPeriod = div(numSampPerPeriod,numBlockAverages*numPeriodsPerPatch) - frames = zeros(Float32, numTrueSampPerPeriod, numChan, numPeriods*numPeriodsPerPatch, numFrames) - convertSamplesToFrames!(samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages, numPeriodsPerPatch) + numTrueSampPerPeriod = div(numSampPerPeriod, numBlockAverages * numPeriodsPerPatch) + frames = zeros(Float32, numTrueSampPerPeriod, numChan, numPeriods * numPeriodsPerPatch, numFrames) + convertSamplesToFrames!( + samples, + frames, + numChan, + numSampPerPeriod, + numPeriods, + numFrames, + numTrueSampPerPeriod, + numBlockAverages, + numPeriodsPerPatch, + ) return frames end """ - convertSamplesToFrames!(rpu::Union{RedPitayaCluster, RedPitayaClusterView}, samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages=1, numPeriodsPerPatch=1) - Converts a given set of samples to frames in-place. See [`readFrames`](@ref) """ function convertSamplesToFrames!(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages=1, numPeriodsPerPatch=1) convertSamplesToFrames!(samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages, numPeriodsPerPatch) - calibs = [x.calib for x in rpu] + calibs = [x.calib for x ∈ rpu] calib = hcat(calibs...) - for d = 1:size(frames, 2) + for d ∈ 1:size(frames, 2) frames[:, d, :, :] .*= calib[1, d] frames[:, d, :, :] .+= calib[2, d] end end """ - convertSamplesToFrames!(samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages=1, numPeriodsPerPatch=1) - Converts a given set of samples to frames in-place. See [`readFrames`](@ref) """ function convertSamplesToFrames!(samples, frames, numChan, numSampPerPeriod, numPeriods, numFrames, numTrueSampPerPeriod, numBlockAverages=1, numPeriodsPerPatch=1) temp = reshape(samples, numChan, numSampPerPeriod, numPeriods, numFrames) - for d = 1:div(numChan,2) - u = temp[2*d-1:2*d, :, :, :] - utmp1 = reshape(u,2,numTrueSampPerPeriod,numBlockAverages, size(u,3)*numPeriodsPerPatch,size(u,4)) - utmp2 = numBlockAverages > 1 ? mean(utmp1,dims=3) : utmp1 - frames[:,2*d-1,:,:] = utmp2[1,:,1,:,:] - frames[:,2*d,:,:] = utmp2[2,:,1,:,:] + for d ∈ 1:div(numChan, 2) + u = temp[(2 * d - 1):(2 * d), :, :, :] + utmp1 = reshape(u, 2, numTrueSampPerPeriod, numBlockAverages, size(u, 3) * numPeriodsPerPatch, size(u, 4)) + utmp2 = numBlockAverages > 1 ? mean(utmp1; dims = 3) : utmp1 + frames[:, 2 * d - 1, :, :] = utmp2[1, :, 1, :, :] + frames[:, 2 * d, :, :] = utmp2[2, :, 1, :, :] end end """ - readPeriods(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, startPeriod, numPeriods, numBlockAverages=1, numPeriodsPerPatch=1; rpInfo=nothing, chunkSize = 50000, useCalibration = false) - Request and receive `numPeriods` Periods from `startPeriod` on. See [`readSamples`](@ref), [`convertSamplesToPeriods!`](@ref), [`samplesPerPeriod`](@ref). # Arguments -- `rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}`: `RedPitaya`s to receive samples from. -- `startPeriod`: period from which to start transmitting -- `numPeriods`: number of periods to read -- `numBlockAverages=1`: see `convertSamplesToPeriods` -- `chunkSize=50000`: see `readSamples` -- `rpInfo=nothing`: see `readSamples` -- `useCalibration`: convert samples based on `RedPitaya`s calibration + + - `rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}`: `RedPitaya`s to receive samples from. + - `startPeriod`: period from which to start transmitting + - `numPeriods`: number of periods to read + - `numBlockAverages=1`: see `convertSamplesToPeriods` + - `chunkSize=50000`: see `readSamples` + - `rpInfo=nothing`: see `readSamples` + - `useCalibration`: convert samples based on `RedPitaya`s calibration """ -function readPeriods(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView}, startPeriod, numPeriods, numBlockAverages=1; rpInfo=nothing, chunkSize = 50000, useCalibration = false, correct_filter_delay::Bool = true) +function readPeriods( + rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, + startPeriod, + numPeriods, + numBlockAverages = 1; + rpInfo = nothing, + chunkSize = 50000, + useCalibration = false, + correct_filter_delay::Bool = true, +) numSampPerPeriod = samplesPerPeriod(rpu) - if rem(numSampPerPeriod,numBlockAverages) != 0 + if rem(numSampPerPeriod, numBlockAverages) != 0 error("block averages has to be a divider of numSampPerPeriod") end - numAveragedSampPerPeriod = div(numSampPerPeriod,numBlockAverages) + numAveragedSampPerPeriod = div(numSampPerPeriod, numBlockAverages) data = zeros(Float32, numAveragedSampPerPeriod, numChan(rpu), numPeriods) wpStart = startPeriod * numSampPerPeriod numOfRequestedSamples = numPeriods * numSampPerPeriod # rawSamples Int16 numofChan(rpc) x numOfRequestedSamples - rawSamples = readSamples(rpu, Int64(wpStart), Int64(numOfRequestedSamples), chunkSize = chunkSize, rpInfo = rpInfo, correct_filter_delay = correct_filter_delay) + rawSamples = readSamples( + rpu, + Int64(wpStart), + Int64(numOfRequestedSamples); + chunkSize = chunkSize, + rpInfo = rpInfo, + correct_filter_delay = correct_filter_delay, + ) # Reshape/Avg Data if useCalibration - convertSamplesToPeriods!(rpu, rawSamples, data, numChan(rpu), numSampPerPeriod, numPeriods, numBlockAverages) + convertSamplesToPeriods!( + rpu, + rawSamples, + data, + numChan(rpu), + numSampPerPeriod, + numPeriods, + numBlockAverages, + ) else convertSamplesToPeriods!(rawSamples, data, numChan(rpu), numSampPerPeriod, numPeriods, numBlockAverages) end @@ -321,38 +405,33 @@ function readPeriods(rpu::Union{RedPitaya,RedPitayaCluster, RedPitayaClusterView end """ - convertSamplesToPeriods!(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages=1) - Converts a given set of samples to periods in-place. See [`readPeriods`](@ref) """ function convertSamplesToPeriods!(rpu::Union{RedPitaya, RedPitayaCluster, RedPitayaClusterView}, samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages=1) convertSamplesToPeriods!(samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages) - calibs = [x.calib for x in rpu] + calibs = [x.calib for x ∈ rpu] calib = hcat(calibs...) - for d = 1:size(periods, 2) + for d ∈ 1:size(periods, 2) periods[:, d, :] .*= calib[1, d] periods[:, d, :] .+= calib[2, d] end return periods - end """ - convertSamplesToPeriods!(samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages=1) - Converts a given set of samples to periods in-place. See [`readPeriods`](@ref) """ function convertSamplesToPeriods!(samples, periods, numChan, numSampPerPeriod, numPeriods, numBlockAverages=1) temp = reshape(samples, numChan, numSampPerPeriod, numPeriods) - for d = 1:div(numChan,2) - u = temp[2*d-1:2*d, :, :] - utmp1 = reshape(u,2,div(numSampPerPeriod,numBlockAverages), numBlockAverages, size(u,3)) - utmp2 = numBlockAverages > 1 ? mean(utmp1,dims=3) : utmp1 - periods[:,2*d-1,:] = utmp2[1,:,1,:] - periods[:,2*d,:] = utmp2[2,:,1,:] + for d ∈ 1:div(numChan, 2) + u = temp[(2 * d - 1):(2 * d), :, :] + utmp1 = reshape(u, 2, div(numSampPerPeriod, numBlockAverages), numBlockAverages, size(u, 3)) + utmp2 = numBlockAverages > 1 ? mean(utmp1; dims = 3) : utmp1 + periods[:, 2 * d - 1, :] = utmp2[1, :, 1, :] + periods[:, 2 * d, :] = utmp2[2, :, 1, :] end end diff --git a/src/client/julia/src/Cluster.jl b/src/client/julia/src/Cluster.jl index 0c7a53e0..322b11c9 100644 --- a/src/client/julia/src/Cluster.jl +++ b/src/client/julia/src/Cluster.jl @@ -6,8 +6,6 @@ export ClusterTriggerSetup, ALL_INTERNAL, ALL_EXTERNAL, MASTER_EXTERNAL @enum ClusterTriggerSetup ALL_INTERNAL ALL_EXTERNAL MASTER_EXTERNAL """ - RedPitayaCluster - Struct representing a cluster of `RedPitaya`s. Such a cluster should share a common clock and master trigger. The structure implements the indexing and iterable interfaces. @@ -17,8 +15,6 @@ struct RedPitayaCluster end """ - RedPitayaCluster(hosts::Vector{String} [, port = 5025]) - Construct a `RedPitayaCluster`. During the construction the first host is labelled the master RedPitaya of a cluster and all RedPitayas @@ -27,6 +23,7 @@ are set to using the `EXTERNAL` trigger mode. See also [`RedPitaya`](@ref), [`master`](@ref). # Examples + ```julia julia> rpc = RedPitayaCluster(["192.168.1.100", "192.168.1.101"]); @@ -36,9 +33,14 @@ julia> rp == rpc[1] true ``` """ -function RedPitayaCluster(hosts::Vector{String}, port::Int64=5025, dataPort::Int64=5026; triggerMode::ClusterTriggerSetup=ALL_INTERNAL) +function RedPitayaCluster( + hosts::Vector{String}, + port::Int64 = 5025, + dataPort::Int64 = 5026; + triggerMode::ClusterTriggerSetup = ALL_INTERNAL, +) # the first RP is the master - rps = RedPitaya[ RedPitaya(host, port, dataPort, i==1) for (i,host) in enumerate(hosts) ] + rps = RedPitaya[RedPitaya(host, port, dataPort, i == 1) for (i, host) ∈ enumerate(hosts)] modes = nothing if triggerMode == ALL_INTERNAL @@ -49,33 +51,29 @@ function RedPitayaCluster(hosts::Vector{String}, port::Int64=5025, dataPort::Int modes = fill(INTERNAL, length(rps)) modes[1] = EXTERNAL end - @sync for (i, rp) in enumerate(rps) - @async begin + @sync for (i, rp) ∈ enumerate(rps) + @async begin triggerMode!(rp, modes[i]) triggerPropagation!(rp, true) end end triggerPropagation!(rps[end], false) - + return RedPitayaCluster(rps) end """ - length(rpc::RedPitayaCluster) - Return the number of `RedPitaya`s in cluster `rpc`. """ length(rpc::RedPitayaCluster) = length(rpc.rp) -""" - numChan(rpc::RedPitayaCluster) -Return the number of ADC channel in cluser `rpc`. """ -numChan(rpc::RedPitayaCluster) = 2*length(rpc) +Return the number of ADC channel in cluser `rpc`. """ - master(rpc::RedPitayaCluster) +numChan(rpc::RedPitayaCluster) = 2 * length(rpc) +""" Return the master `RedPitaya` of the cluster. """ master(rpc::RedPitayaCluster) = rpc.rp[1] @@ -90,28 +88,43 @@ lastindex(rpc::RedPitayaCluster) = length(rpc) # Iterable Interface start_(rpc::RedPitayaCluster) = 1 -next_(rpc::RedPitayaCluster,state) = (rpc[state],state+1) -done_(rpc::RedPitayaCluster,state) = state > length(rpc) -iterate(rpc::RedPitayaCluster, s=start_(rpc)) = done_(rpc, s) ? nothing : next_(rpc, s) - -batchIndices(f::Function, rpc::RedPitaya, args...) = error("Function $(string(f)) is not supported in cluster batch or has incorrect parameters.") -batchTransformArgs(::Function, rpc::RedPitayaCluster, idx, args...) = args +next_(rpc::RedPitayaCluster, state) = (rpc[state], state + 1) +done_(rpc::RedPitayaCluster, state) = state > length(rpc) +iterate(rpc::RedPitayaCluster, s = start_(rpc)) = done_(rpc, s) ? nothing : next_(rpc, s) -function RPInfo(rpc::RedPitayaCluster) - return RPInfo([RPPerformance([]) for i = 1:length(rpc)]) +function batchIndices(f::Function, rpc::RedPitaya, args...) + return error("Function $(string(f)) is not supported in cluster batch or has incorrect parameters.") end +batchTransformArgs(::Function, rpc::RedPitayaCluster, idx, args...) = args -for op in [:currentFrame, :currentPeriod, :currentWP, :periodsPerFrame, :samplesPerPeriod, :decimation, :keepAliveReset, - :triggerMode, :samplesPerStep, :serverMode, :masterTrigger, - :counterTrigger_enabled, :counterTrigger_enabled!, :counterTrigger_presamples, - :counterTrigger_isArmed, :counterTrigger_arm!, :counterTrigger_reset!, - :counterTrigger_reset, :counterTrigger_lastCounter, :counterTrigger_referenceCounter, - :counterTrigger_sourceType, :counterTrigger_sourceChannel] - +RPInfo(rpc::RedPitayaCluster) = RPInfo([RPPerformance([]) for i ∈ 1:length(rpc)]) + +for op ∈ [ + :currentFrame, + :currentPeriod, + :currentWP, + :periodsPerFrame, + :samplesPerPeriod, + :decimation, + :keepAliveReset, + :triggerMode, + :samplesPerStep, + :serverMode, + :masterTrigger, + :counterTrigger_enabled, + :counterTrigger_enabled!, + :counterTrigger_presamples, + :counterTrigger_isArmed, + :counterTrigger_arm!, + :counterTrigger_reset!, + :counterTrigger_reset, + :counterTrigger_lastCounter, + :counterTrigger_referenceCounter, + :counterTrigger_sourceType, + :counterTrigger_sourceChannel, +] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster) - As with single RedPitaya, but applied to only the master. """ $op(rpc::RedPitayaCluster) = $op(master(rpc)) @@ -119,20 +132,30 @@ for op in [:currentFrame, :currentPeriod, :currentWP, :periodsPerFrame, :samples end end -for op in [:periodsPerFrame!, :samplesPerPeriod!, :decimation!, :triggerMode!, :samplesPerStep!, - :keepAliveReset!, :serverMode!, :stopTransmission, - :counterTrigger_enabled!, :counterTrigger_presamples!, :counterTrigger_arm!, - :counterTrigger_reset!, :counterTrigger_referenceCounter!, - :counterTrigger_sourceType!, :counterTrigger_sourceChannel!] +for op ∈ [ + :periodsPerFrame!, + :samplesPerPeriod!, + :decimation!, + :triggerMode!, + :samplesPerStep!, + :keepAliveReset!, + :serverMode!, + :stopTransmission, + :counterTrigger_enabled!, + :counterTrigger_presamples!, + :counterTrigger_arm!, + :counterTrigger_reset!, + :counterTrigger_referenceCounter!, + :counterTrigger_sourceType!, + :counterTrigger_sourceChannel!, +] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster, value) - As with single RedPitaya, but applied to all RedPitayas in a cluster. """ function $op(rpc::RedPitayaCluster, value) - result = [false for i = 1:length(rpc)] - @sync for (i, rp) in enumerate(rpc) + result = [false for i ∈ 1:length(rpc)] + @sync for (i, rp) ∈ enumerate(rpc) @async result[i] = $op(rp, value) end return result @@ -141,16 +164,14 @@ for op in [:periodsPerFrame!, :samplesPerPeriod!, :decimation!, :triggerMode!, : end end -for op in [:clearSequence!, :sequence!] +for op ∈ [:clearSequence!, :sequence!] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster) - As with single RedPitaya, but applied to all RedPitayas in a cluster. """ function $op(rpc::RedPitayaCluster) - result = [false for i = 1:length(rpc)] - @sync for (i, rp) in enumerate(rpc) + result = [false for i ∈ 1:length(rpc)] + @sync for (i, rp) ∈ enumerate(rpc) @async result[i] = $op(rp) end return result @@ -159,25 +180,19 @@ for op in [:clearSequence!, :sequence!] end end -for op in [:disconnect, :connect] +for op ∈ [:disconnect, :connect] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster) - As with single RedPitaya, but applied to all RedPitayas in a cluster. """ - function $op(rpc::RedPitayaCluster) - @sync for rp in rpc - @async $op(rp) - end + $op(rpc::RedPitayaCluster) = @sync for rp ∈ rpc + @async $op(rp) end batchIndices(::typeof($op), rpc::RedPitayaCluster) = collect(1:length(rpc)) end end """ - masterTrigger(rpc::RedPitayaCluster, val::Bool) - Set the master trigger of the cluster to `val`. For `val` equals to true this is the same as calling the function on the RedPitaya returned by `master(rpc)`. @@ -187,91 +202,86 @@ before the master trigger is disabled. Afterwards the keepAliveReset is set to f See also [`master`](@ref), [`keepAliveReset!`](@ref). """ function masterTrigger!(rpc::RedPitayaCluster, val::Bool) - if val - masterTrigger!(master(rpc), val) - else - keepAliveReset!(rpc, true) - masterTrigger!(master(rpc), false) - keepAliveReset!(rpc, false) - end - return masterTrigger(rpc) + if val + masterTrigger!(master(rpc), val) + else + keepAliveReset!(rpc, true) + masterTrigger!(master(rpc), false) + keepAliveReset!(rpc, false) + end + return masterTrigger(rpc) end - -for op in [:signalTypeDAC, :amplitudeDAC, :frequencyDAC, :phaseDAC] +for op ∈ [:signalTypeDAC, :amplitudeDAC, :frequencyDAC, :phaseDAC] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster, chan::Integer, component::Integer) - As with single RedPitaya. The `chan` index refers to the total channel available in a cluster, two per `RedPitaya`. For example channel `4` would refer to the second channel of the second `RedPitaya`. """ function $op(rpc::RedPitayaCluster, chan::Integer, component::Integer) - idxRP = div(chan-1, 2) + 1 + idxRP = div(chan - 1, 2) + 1 chanRP = mod1(chan, 2) return $op(rpc[idxRP], chanRP, component) end - batchIndices(::typeof($op), rpc::RedPitayaCluster, chan, component) = [div(chan -1, 2) + 1] - batchTransformArgs(::typeof($op), rpc::RedPitayaCluster, idx, chan, component) = (mod1(chan, 2), component) + batchIndices(::typeof($op), rpc::RedPitayaCluster, chan, component) = [div(chan - 1, 2) + 1] + function batchTransformArgs(::typeof($op), rpc::RedPitayaCluster, idx, chan, component) + return (mod1(chan, 2), component) + end end end -for op in [:signalTypeDAC!, :amplitudeDAC!, :frequencyDAC!, :phaseDAC!] +for op ∈ [:signalTypeDAC!, :amplitudeDAC!, :frequencyDAC!, :phaseDAC!] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster, chan::Integer, component::Integer, value) - As with single RedPitaya. The `chan` index refers to the total channel available in a cluster, two per `RedPitaya`. For example channel `4` would refer to the second channel of the second `RedPitaya`. """ function $op(rpc::RedPitayaCluster, chan::Integer, component::Integer, value) - idxRP = div(chan-1, 2) + 1 + idxRP = div(chan - 1, 2) + 1 chanRP = mod1(chan, 2) return $op(rpc[idxRP], chanRP, component, value) end - batchIndices(::typeof($op), rpc::RedPitayaCluster, chan, component, value) = [div(chan -1, 2) + 1] - batchTransformArgs(::typeof($op), rpc::RedPitayaCluster, idx, chan, component, value) = (mod1(chan, 2), component, value) + batchIndices(::typeof($op), rpc::RedPitayaCluster, chan, component, value) = [div(chan - 1, 2) + 1] + function batchTransformArgs(::typeof($op), rpc::RedPitayaCluster, idx, chan, component, value) + return (mod1(chan, 2), component, value) + end end end -for op in [:offsetDAC, :rampingDAC, :enableRamping, :enableRampDown] +for op ∈ [:offsetDAC, :rampingDAC, :enableRamping, :enableRampDown] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster, chan::Integer) - As with single RedPitaya. The `chan` index refers to the total channel available in a cluster, two per `RedPitaya`. For example channel `4` would refer to the second channel of the second `RedPitaya`. """ function $op(rpc::RedPitayaCluster, chan::Integer) - idxRP = div(chan-1, 2) + 1 + idxRP = div(chan - 1, 2) + 1 chanRP = mod1(chan, 2) return $op(rpc[idxRP], chanRP) end - batchIndices(::typeof($op), rpc::RedPitayaCluster, chan) = [div(chan -1, 2) + 1] + batchIndices(::typeof($op), rpc::RedPitayaCluster, chan) = [div(chan - 1, 2) + 1] batchTransformArgs(::typeof($op), rpc::RedPitayaCluster, idx, chan) = (mod1(chan, 2)) end end -for op in [:offsetDAC!, :rampingDAC!, :enableRamping!, :enableRampDown!] +for op ∈ [:offsetDAC!, :rampingDAC!, :enableRamping!, :enableRampDown!] @eval begin @doc """ - $($op)(rpc::RedPitayaCluster, chan::Integer, value) - As with single RedPitaya. The `chan` index refers to the total channel available in a cluster, two per `RedPitaya`. For example channel `4` would refer to the second channel of the second `RedPitaya`. """ function $op(rpc::RedPitayaCluster, chan::Integer, value) - idxRP = div(chan-1, 2) + 1 + idxRP = div(chan - 1, 2) + 1 chanRP = mod1(chan, 2) return $op(rpc[idxRP], chanRP, value) end - batchIndices(::typeof($op), rpc::RedPitayaCluster, chan, value) = [div(chan -1, 2) + 1] + batchIndices(::typeof($op), rpc::RedPitayaCluster, chan, value) = [div(chan - 1, 2) + 1] batchTransformArgs(::typeof($op), rpc::RedPitayaCluster, idx, chan, value) = (mod1(chan, 2), value) end end -for op in [:waveformDAC!, :scaleWaveformDAC!] +for op ∈ [:waveformDAC!, :scaleWaveformDAC!] @eval begin function $op(rpc::RedPitayaCluster, channel::Integer, value) - idxRP = div(channel-1, 2) + 1 + idxRP = div(channel - 1, 2) + 1 chan = mod1(channel, 2) return $op(rpc[idxRP], chan, value) end @@ -280,18 +290,18 @@ end function rampingStatus(rpc::RedPitayaCluster) result = Array{RampingStatus}(undef, length(rpc)) - @sync for (d, rp) in enumerate(rpc) + @sync for (d, rp) ∈ enumerate(rpc) @async result[d] = rampingStatus(rp) end return result end batchIndices(::typeof(rampingStatus), rpc::RedPitayaCluster) = collect(1:length(rpc)) -for op in [:rampDownDone, :rampUpDone] +for op ∈ [:rampDownDone, :rampUpDone] @eval begin function $op(rpc::RedPitayaCluster) - result = [false for i = 1:length(rpc)] - @sync for (d, rp) in enumerate(rpc) + result = [false for i ∈ 1:length(rpc)] + @sync for (d, rp) ∈ enumerate(rpc) @async result[d] = $op(rp) end return all(result) @@ -300,8 +310,6 @@ for op in [:rampDownDone, :rampUpDone] end """ - execute!(rpc::RedPitayaCluster, batch::ScpiBatch) - Executes all commands of the given batch. Returns an array of the results in the order of the commands. Each element of the result array is again an array containing the return values of the RedPitayas. @@ -309,14 +317,14 @@ An element of an inner array is `nothing` if the command has no return value. """ function execute!(rpc::RedPitayaCluster, batch::ScpiBatch) # Send cmd after cmd to each "affected" RedPitaya - cmds = [Vector{String}() for i =1:length(rpc)] - for (f, args) in batch.cmds + cmds = [Vector{String}() for i ∈ 1:length(rpc)] + for (f, args) ∈ batch.cmds indices = batchIndices(f, rpc, args...) - for idx in indices + for idx ∈ indices push!(cmds[idx], scpiCommand(f, batchTransformArgs(f, rpc, idx, args...)...)) end end - @sync for (i, cmd) in enumerate(cmds) + @sync for (i, cmd) ∈ enumerate(cmds) @async begin if !isempty(cmd) cmdStr = join(cmd, rpc[i].delim) @@ -327,10 +335,10 @@ function execute!(rpc::RedPitayaCluster, batch::ScpiBatch) end results = [] # Retrieve results from each "affected" RedPitaya for each cmd - for (f, args) in batch.cmds + for (f, args) ∈ batch.cmds result = Array{Union{Nothing, scpiReturn(f)}}(nothing, length(rpc)) indices = batchIndices(f, rpc, args...) - @sync for idx in indices + @sync for idx ∈ indices @async begin if !isnothing(scpiReturn(f)) ret = parseReturn(f, receive(rpc[idx], getTimeout())) @@ -345,17 +353,18 @@ function execute!(rpc::RedPitayaCluster, batch::ScpiBatch) end """ - execute!(f::Function, rp::Union{RedPitaya, RedPitayaCluster}) - Open a `ScpiBatch` and evaluate the function `f`. If no exception was thrown, execute the opened batch. See also [`ScpiBatch`](@ref), [`push!`](@ref), [`@add_batch`](@ref) + # Examples + ```julia -julia> execute!(rp) do b - @add_batch b serverMode!(rp, CONFIGURATION) - @add_batch b amplitudeDAC!(rp, 1, 1, 0.2) - end +julia> execute!(rp) do b + @add_batch b serverMode!(rp, CONFIGURATION) + @add_batch b amplitudeDAC!(rp, 1, 1, 0.2) + end + ``` """ function execute!(f::Function, rp::Union{RedPitaya, RedPitayaCluster}) diff --git a/src/client/julia/src/ClusterView.jl b/src/client/julia/src/ClusterView.jl index d8a15a4c..dc235141 100644 --- a/src/client/julia/src/ClusterView.jl +++ b/src/client/julia/src/ClusterView.jl @@ -1,28 +1,30 @@ -export RedPitayaClusterView, master, numChan, readFrames, readPeriods, startPipelinedData, collectSamples!, viewToCluster, clusterToView +export RedPitayaClusterView, + master, numChan, readFrames, readPeriods, startPipelinedData, collectSamples!, viewToCluster, clusterToView import Base: length, iterate, getindex, firstindex, lastindex struct RedPitayaClusterView - rpc::RedPitayaCluster - view::Vector{Integer} + rpc::RedPitayaCluster + view::Vector{Integer} end function RedPitayaClusterView(rpc::RedPitayaCluster, selection::Vector{Bool}) - length(selection) == length(rpc) || throw(DimensionMismatch("Length of boolean vector must match length of cluster")) - view = findall(selection) - return RedPitayaClusterView(rpc, view) + length(selection) == length(rpc) || + throw(DimensionMismatch("Length of boolean vector must match length of cluster")) + view = findall(selection) + return RedPitayaClusterView(rpc, view) end - + length(rpcv::RedPitayaClusterView) = length(rpcv.view) numChan(rpcv::RedPitayaClusterView) = 2 * length(rpcv.view) master(rpcv::RedPitayaClusterView) = master(rpcv.rpc) - + # Index function getindex(rpcv::RedPitayaClusterView, index::Integer) - 1 <= index <= length(rpcv) || throw(BoundsError(rpcv.view, index)) - viewIndex = rpcv.view[index] - 1 <= viewIndex <= length(rpcv.rpc) || throw(BoundsError(rpcv.rpc, viewIndex)) - return rpcv.rpc[viewIndex] + 1 <= index <= length(rpcv) || throw(BoundsError(rpcv.view, index)) + viewIndex = rpcv.view[index] + 1 <= viewIndex <= length(rpcv.rpc) || throw(BoundsError(rpcv.rpc, viewIndex)) + return rpcv.rpc[viewIndex] end firstindex(rpcv::RedPitayaClusterView) = start_(rpcv) lastindex(rpcv::RedPitayaClusterView) = length(rpcv.view) @@ -31,27 +33,25 @@ lastindex(rpcv::RedPitayaClusterView) = length(rpcv.view) start_(rpcv::RedPitayaClusterView) = 1 next_(rpcv::RedPitayaClusterView, state) = (rpcv[state], state + 1) done_(rpcv::RedPitayaClusterView, state) = state > length(rpcv) -iterate(rpcv::RedPitayaClusterView, s=start_(rpcv)) = done_(rpcv, s) ? nothing : next_(rpcv, s) +iterate(rpcv::RedPitayaClusterView, s = start_(rpcv)) = done_(rpcv, s) ? nothing : next_(rpcv, s) # Returns the channel number in the cluster for a given channel number in the view function viewToCluster(rpcv::RedPitayaClusterView, chan::Integer) - view = rpcv.view - idxInView = div(chan -1, 2) + 1 - chanRP = mod1(chan, 2) - return 2*(view[idxInView] - 1) + chanRP + view = rpcv.view + idxInView = div(chan - 1, 2) + 1 + chanRP = mod1(chan, 2) + return 2 * (view[idxInView] - 1) + chanRP end function clusterToView(rpcv::RedPitayaClusterView, chan::Integer) - for (i, v) in enumerate(rpcv.view) - if 2*v-1 == chan - return 2*i -1 - elseif 2*v == chan - return 2*i - end + for (i, v) ∈ enumerate(rpcv.view) + if 2 * v - 1 == chan + return 2 * i - 1 + elseif 2 * v == chan + return 2 * i end - return nothing + end + return nothing end -function currentWP(rpcv::RedPitayaClusterView) - return currentWP(rpcv.rpc) -end +currentWP(rpcv::RedPitayaClusterView) = currentWP(rpcv.rpc) diff --git a/src/client/julia/src/CounterTrigger.jl b/src/client/julia/src/CounterTrigger.jl index d627e926..ceb1d683 100644 --- a/src/client/julia/src/CounterTrigger.jl +++ b/src/client/julia/src/CounterTrigger.jl @@ -1,10 +1,9 @@ export counterTrigger_enabled """ - counterTrigger_enabled(rp::RedPitaya) - Return whether the counter trigger is enabled or not. # Examples + ```julia julia> counterTrigger_enabled!(rp, true) true @@ -13,18 +12,19 @@ julia> counterTrigger_enabled(rp) true ``` """ -counterTrigger_enabled(rp::RedPitaya) = parseReturn(counterTrigger_enabled, query(rp, scpiCommand(counterTrigger_enabled))) +function counterTrigger_enabled(rp::RedPitaya) + return parseReturn(counterTrigger_enabled, query(rp, scpiCommand(counterTrigger_enabled))) +end scpiCommand(::typeof(counterTrigger_enabled)) = "RP:CounterTrigger:ENable?" scpiReturn(::typeof(counterTrigger_enabled)) = String parseReturn(::typeof(counterTrigger_enabled), ret) = occursin("ON", ret) export counterTrigger_enabled! """ -counterTrigger_enabled!(rp::RedPitaya, val) - Set whether the counter trigger is enabled or not. Return `true` if the command was successful. # Examples + ```julia julia> counterTrigger_enabled!(rp, true) true @@ -41,11 +41,10 @@ scpiReturn(::typeof(counterTrigger_enabled!)) = Bool export counterTrigger_presamples """ - counterTrigger_presamples(rp::RedPitaya) - Return the number of samples that the counter trigger should trigger prior to reaching the reference counter. # Examples + ```julia julia> counterTrigger_presamples!(rp, 50) true @@ -54,17 +53,18 @@ julia> counterTrigger_presamples(rp) 50 ``` """ -counterTrigger_presamples(rp::RedPitaya) = query(rp, scpiCommand(counterTrigger_presamples), scpiReturn(counterTrigger_presamples)) +function counterTrigger_presamples(rp::RedPitaya) + return query(rp, scpiCommand(counterTrigger_presamples), scpiReturn(counterTrigger_presamples)) +end scpiCommand(::typeof(counterTrigger_presamples)) = "RP:CounterTrigger:PREsamples?" scpiReturn(::typeof(counterTrigger_presamples)) = Int64 export counterTrigger_presamples! """ - counterTrigger_presamples!(rp::RedPitaya, presamples) - Set the number of samples that the counter trigger should trigger prior to reaching the reference counter. # Examples + ```julia julia> counterTrigger_presamples!(rp, 50) true @@ -73,19 +73,24 @@ julia> counterTrigger_presamples(rp) 50 ``` """ -function counterTrigger_presamples!(rp::RedPitaya, presamples::T) where T <: Integer - return query(rp, scpiCommand(counterTrigger_presamples!, presamples), scpiReturn(counterTrigger_presamples!)) +function counterTrigger_presamples!(rp::RedPitaya, presamples::T) where {T <: Integer} + return query( + rp, + scpiCommand(counterTrigger_presamples!, presamples), + scpiReturn(counterTrigger_presamples!), + ) +end +function scpiCommand(::typeof(counterTrigger_presamples!), presamples) + return string("RP:CounterTrigger:PREsamples ", presamples) end -scpiCommand(::typeof(counterTrigger_presamples!), presamples) = string("RP:CounterTrigger:PREsamples ", presamples) scpiReturn(::typeof(counterTrigger_presamples!)) = Bool export counterTrigger_isArmed """ - counterTrigger_isArmed(rp::RedPitaya) - Return whether the counter trigger is armed or not. # Examples + ```julia julia> counterTrigger_arm!(rp, true) true @@ -94,17 +99,18 @@ julia> counterTrigger_isArmed(rp) true ``` """ -counterTrigger_isArmed(rp::RedPitaya) = query(rp, scpiCommand(counterTrigger_isArmed), scpiReturn(counterTrigger_isArmed)) +function counterTrigger_isArmed(rp::RedPitaya) + return query(rp, scpiCommand(counterTrigger_isArmed), scpiReturn(counterTrigger_isArmed)) +end scpiCommand(::typeof(counterTrigger_isArmed)) = "RP:CounterTrigger:ARM?" scpiReturn(::typeof(counterTrigger_isArmed)) = Bool export counterTrigger_arm! """ - counterTrigger_arm!(rp::RedPitaya, val::Bool) - Set whether the counter trigger is armed or not. Return `true` if the command was successful. # Examples + ```julia julia> counterTrigger_arm!(rp, true) true @@ -113,7 +119,7 @@ julia> counterTrigger_isArmed(rp) true ``` """ -function counterTrigger_arm!(rp::RedPitaya, val=true) +function counterTrigger_arm!(rp::RedPitaya, val = true) return query(rp, scpiCommand(counterTrigger_arm!, val), scpiReturn(counterTrigger_arm!)) end scpiCommand(::typeof(counterTrigger_arm!), val::Bool) = string("RP:CounterTrigger:ARM ", val ? "ON" : "OFF") @@ -121,11 +127,10 @@ scpiReturn(::typeof(counterTrigger_arm!)) = Bool export counterTrigger_reset """ - counterTrigger_reset(rp::RedPitaya) - Return the reset status of the counter trigger. # Example + ```julia julia> counterTrigger_reset!(rp, true) @@ -133,18 +138,19 @@ julia>counterTrigger_reset(rp) true ``` """ -counterTrigger_reset(rp::RedPitaya) = parseReturn(counterTrigger_reset, query(rp, scpiCommand(counterTrigger_reset))) +function counterTrigger_reset(rp::RedPitaya) + return parseReturn(counterTrigger_reset, query(rp, scpiCommand(counterTrigger_reset))) +end scpiCommand(::typeof(counterTrigger_reset)) = "RP:CounterTrigger:RESet?" scpiReturn(::typeof(counterTrigger_reset)) = String parseReturn(::typeof(counterTrigger_reset), ret) = occursin("ON", ret) export counterTrigger_reset! """ - counterTrigger_reset!(rp::RedPitaya, val::Bool) - Set the reset of the counter trigger to `val`. Return `true` if the command was successful. # Example + ```julia julia> counterTrigger_reset!(rp, true) true @@ -153,39 +159,43 @@ julia>counterTrigger_reset(rp) true ``` """ -counterTrigger_reset!(rp::RedPitaya, val) = query(rp, scpiCommand(counterTrigger_reset!, val), scpiReturn(counterTrigger_reset!)) +function counterTrigger_reset!(rp::RedPitaya, val) + return query(rp, scpiCommand(counterTrigger_reset!, val), scpiReturn(counterTrigger_reset!)) +end function counterTrigger_reset!(rp::RedPitaya) counterTrigger_reset!(rp, true) sleep(0.05) - counterTrigger_reset!(rp, false) + return counterTrigger_reset!(rp, false) +end +function scpiCommand(::typeof(counterTrigger_reset!), val::Bool) + return scpiCommand(counterTrigger_reset!, val ? "ON" : "OFF") end -scpiCommand(::typeof(counterTrigger_reset!), val::Bool) = scpiCommand(counterTrigger_reset!, val ? "ON" : "OFF") scpiCommand(::typeof(counterTrigger_reset!), val::String) = string("RP:CounterTrigger:RESet ", val) scpiReturn(::typeof(counterTrigger_reset!)) = Bool export counterTrigger_lastCounter """ -counterTrigger_lastCounter(rp::RedPitaya) - Return the number of samples that the counter trigger should trigger prior to reaching the reference counter. # Examples + ```julia julia> counterTrigger_lastCounter(rp) 123456 ``` """ -counterTrigger_lastCounter(rp::RedPitaya) = query(rp, scpiCommand(counterTrigger_lastCounter), scpiReturn(counterTrigger_lastCounter)) +function counterTrigger_lastCounter(rp::RedPitaya) + return query(rp, scpiCommand(counterTrigger_lastCounter), scpiReturn(counterTrigger_lastCounter)) +end scpiCommand(::typeof(counterTrigger_lastCounter)) = "RP:CounterTrigger:COUNTer:LAst?" scpiReturn(::typeof(counterTrigger_lastCounter)) = Int64 export counterTrigger_referenceCounter """ - counterTrigger_referenceCounter(rp::RedPitaya) - Return the counter value that the counter trigger should trigger on. # Examples + ```julia julia> counterTrigger_referenceCounter!(rp, 250) true @@ -194,17 +204,18 @@ julia> counterTrigger_referenceCounter(rp) 250 ``` """ -counterTrigger_referenceCounter(rp::RedPitaya) = query(rp, scpiCommand(counterTrigger_referenceCounter), scpiReturn(counterTrigger_referenceCounter)) +function counterTrigger_referenceCounter(rp::RedPitaya) + return query(rp, scpiCommand(counterTrigger_referenceCounter), scpiReturn(counterTrigger_referenceCounter)) +end scpiCommand(::typeof(counterTrigger_referenceCounter)) = "RP:CounterTrigger:COUNTer:REFerence?" scpiReturn(::typeof(counterTrigger_referenceCounter)) = Int64 export counterTrigger_referenceCounter! """ -counterTrigger_referenceCounter!(rp::RedPitaya, presamples) - Set the number of samples that the counter trigger should trigger on. # Examples + ```julia julia> counterTrigger_referenceCounter(rp, 250) true @@ -213,14 +224,20 @@ julia> counterTrigger_referenceCounter!(rp) 250 ``` """ -counterTrigger_referenceCounter!(rp::RedPitaya, reference::T) where T <: Integer = query(rp, scpiCommand(counterTrigger_referenceCounter!, reference), scpiReturn(counterTrigger_referenceCounter!)) -scpiCommand(::typeof(counterTrigger_referenceCounter!), reference) = string("RP:CounterTrigger:COUNTer:REFerence ", reference) +function counterTrigger_referenceCounter!(rp::RedPitaya, reference::T) where {T <: Integer} + return query( + rp, + scpiCommand(counterTrigger_referenceCounter!, reference), + scpiReturn(counterTrigger_referenceCounter!), + ) +end +function scpiCommand(::typeof(counterTrigger_referenceCounter!), reference) + return string("RP:CounterTrigger:COUNTer:REFerence ", reference) +end scpiReturn(::typeof(counterTrigger_referenceCounter!)) = Bool export CounterTriggerSourceType, COUNTER_TRIGGER_DIO, COUNTER_TRIGGER_ADC """ - CounterTriggerSourceType - Represent the different counter trigger source types. Valid values are `COUNTER_TRIGGER_DIO` and `COUNTER_TRIGGER_ADC`. See [`counterTrigger_sourceType`](@ref), [`counterTrigger_sourceType!`](@ref). @@ -229,11 +246,10 @@ See [`counterTrigger_sourceType`](@ref), [`counterTrigger_sourceType!`](@ref). export counterTrigger_sourceType! """ - counterTrigger_sourceType!(rp::RedPitaya, sourceType::CounterTriggerSourceType) - Set the source type of the counter trigger to `sourceType`. # Example + ```julia julia> counterTrigger_sourceType!(rp, COUNTER_TRIGGER_ADC) @@ -241,17 +257,24 @@ julia>counterTrigger_sourceType(rp) COUNTER_TRIGGER_ADC::CounterTriggerSourceType = 1 ``` """ -counterTrigger_sourceType!(rp::RedPitaya, sourceType) = query(rp, scpiCommand(counterTrigger_sourceType!, sourceType), scpiReturn(counterTrigger_sourceType!)) -scpiCommand(::typeof(counterTrigger_sourceType!), sourceType::CounterTriggerSourceType) = string("RP:CounterTrigger:SouRCe:TYPe ", (sourceType == COUNTER_TRIGGER_DIO ? "DIO" : "ADC")) +function counterTrigger_sourceType!(rp::RedPitaya, sourceType) + return query( + rp, + scpiCommand(counterTrigger_sourceType!, sourceType), + scpiReturn(counterTrigger_sourceType!), + ) +end +function scpiCommand(::typeof(counterTrigger_sourceType!), sourceType::CounterTriggerSourceType) + return string("RP:CounterTrigger:SouRCe:TYPe ", (sourceType == COUNTER_TRIGGER_DIO ? "DIO" : "ADC")) +end scpiReturn(::typeof(counterTrigger_sourceType!)) = Bool export counterTrigger_sourceType """ - counterTrigger_sourceType(rp::RedPitaya) - Get the source type of the counter trigger. # Example + ```julia julia> counterTrigger_sourceType!(rp, COUNTER_TRIGGER_ADC) @@ -259,15 +282,20 @@ julia>counterTrigger_sourceType(rp) COUNTER_TRIGGER_ADC::CounterTriggerSourceType = 1 ``` """ -counterTrigger_sourceType(rp::RedPitaya) = parseReturn(counterTrigger_sourceType, query(rp, scpiCommand(counterTrigger_sourceType), scpiReturn(counterTrigger_sourceType))) +function counterTrigger_sourceType(rp::RedPitaya) + return parseReturn( + counterTrigger_sourceType, + query(rp, scpiCommand(counterTrigger_sourceType), scpiReturn(counterTrigger_sourceType)), + ) +end scpiCommand(::typeof(counterTrigger_sourceType)) = string("RP:CounterTrigger:SouRCe:TYPe?") scpiReturn(::typeof(counterTrigger_sourceType)) = String -parseReturn(::typeof(counterTrigger_sourceType), ret) = stringToEnum(CounterTriggerSourceType, "COUNTER_TRIGGER_"*ret) +function parseReturn(::typeof(counterTrigger_sourceType), ret) + return stringToEnum(CounterTriggerSourceType, "COUNTER_TRIGGER_" * ret) +end export CounterTriggerSourceADCChannel, COUNTER_TRIGGER_IN1, COUNTER_TRIGGER_IN2 """ - CounterTriggerSourceADCChannel - Represent the different counter trigger ADC sources. Valid values are `COUNTER_TRIGGER_IN1` and `COUNTER_TRIGGER_IN2`. See [`counterTrigger_sourceChannel`](@ref), [`counterTrigger_sourceChannel!`](@ref). @@ -276,11 +304,10 @@ See [`counterTrigger_sourceChannel`](@ref), [`counterTrigger_sourceChannel!`](@r export counterTrigger_sourceChannel! """ -counterTrigger_sourceChannel!(rp::RedPitaya, sourceChannel::) //TODO - Set the source channel of the counter trigger to `sourceChannel`. # Example + ```julia julia> counterTrigger_sourceChannel!(rp, COUNTER_TRIGGER_ADC) @@ -288,18 +315,27 @@ julia>counterTrigger_sourceChannel(rp) COUNTER_TRIGGER_ADC::CounterTriggerSourceType = 1 //TODO ``` """ -counterTrigger_sourceChannel!(rp::RedPitaya, sourceChannel) = query(rp, scpiCommand(counterTrigger_sourceChannel!, sourceChannel), scpiReturn(counterTrigger_sourceChannel!)) -scpiCommand(::typeof(counterTrigger_sourceChannel!), sourceChannel::CounterTriggerSourceADCChannel) = string("RP:CounterTrigger:SouRCe:CHANnel ", (sourceChannel == COUNTER_TRIGGER_IN1 ? "IN1" : "IN2")) -scpiCommand(::typeof(counterTrigger_sourceChannel!), sourceChannel::DIOPins) = string("RP:CounterTrigger:SouRCe:CHANnel ", string(sourceChannel)) +function counterTrigger_sourceChannel!(rp::RedPitaya, sourceChannel) + return query( + rp, + scpiCommand(counterTrigger_sourceChannel!, sourceChannel), + scpiReturn(counterTrigger_sourceChannel!), + ) +end +function scpiCommand(::typeof(counterTrigger_sourceChannel!), sourceChannel::CounterTriggerSourceADCChannel) + return string("RP:CounterTrigger:SouRCe:CHANnel ", (sourceChannel == COUNTER_TRIGGER_IN1 ? "IN1" : "IN2")) +end +function scpiCommand(::typeof(counterTrigger_sourceChannel!), sourceChannel::DIOPins) + return string("RP:CounterTrigger:SouRCe:CHANnel ", string(sourceChannel)) +end scpiReturn(::typeof(counterTrigger_sourceChannel!)) = Bool export counterTrigger_sourceChannel """ -counterTrigger_sourceChannel(rp::RedPitaya) - Get the source channel of the counter trigger. # Example + ```julia julia> counterTrigger_sourceChannel!(rp, COUNTER_TRIGGER_IN2) @@ -307,7 +343,18 @@ julia>counterTrigger_sourceChannel(rp) COUNTER_TRIGGER_IN2::CounterTriggerSourceADCChannel = 2 ``` """ -counterTrigger_sourceChannel(rp::RedPitaya) = parseReturn(counterTrigger_sourceChannel, query(rp, scpiCommand(counterTrigger_sourceChannel), scpiReturn(counterTrigger_sourceChannel))) +function counterTrigger_sourceChannel(rp::RedPitaya) + return parseReturn( + counterTrigger_sourceChannel, + query(rp, scpiCommand(counterTrigger_sourceChannel), scpiReturn(counterTrigger_sourceChannel)), + ) +end scpiCommand(::typeof(counterTrigger_sourceChannel)) = string("RP:CounterTrigger:SouRCe:CHANnel?") scpiReturn(::typeof(counterTrigger_sourceChannel)) = String -parseReturn(::typeof(counterTrigger_sourceChannel), ret) = startswith(ret, "DIO") ? stringToEnum(DIOPins, ret) : stringToEnum(CounterTriggerSourceADCChannel, "COUNTER_TRIGGER_"*ret) \ No newline at end of file +function parseReturn(::typeof(counterTrigger_sourceChannel), ret) + return if startswith(ret, "DIO") + stringToEnum(DIOPins, ret) + else + stringToEnum(CounterTriggerSourceADCChannel, "COUNTER_TRIGGER_" * ret) + end +end diff --git a/src/client/julia/src/DAC.jl b/src/client/julia/src/DAC.jl index 3fa3c35d..3c54e24c 100644 --- a/src/client/julia/src/DAC.jl +++ b/src/client/julia/src/DAC.jl @@ -1,11 +1,36 @@ -export SignalType, SINE, TRIANGLE, SAWTOOTH, DACPerformanceData, DACConfig, ArbitraryWaveform, waveformDAC!, scaleWaveformDAC!, -amplitudeDAC, amplitudeDAC!,offsetDAC, offsetDAC!, normalize, -frequencyDAC, frequencyDAC!, phaseDAC, phaseDAC!, signalTypeDAC, signalTypeDAC!, -rampingDAC!, rampingDAC, enableRamping!, enableRamping, enableRampDown, enableRampDown!, RampingState, RampingStatus, rampingStatus, rampDownDone, rampUpDone +export SignalType, + SINE, + TRIANGLE, + SAWTOOTH, + DACPerformanceData, + DACConfig, + ArbitraryWaveform, + waveformDAC!, + scaleWaveformDAC!, + amplitudeDAC, + amplitudeDAC!, + offsetDAC, + offsetDAC!, + normalize, + frequencyDAC, + frequencyDAC!, + phaseDAC, + phaseDAC!, + signalTypeDAC, + signalTypeDAC!, + rampingDAC!, + rampingDAC, + enableRamping!, + enableRamping, + enableRampDown, + enableRampDown!, + RampingState, + RampingStatus, + rampingStatus, + rampDownDone, + rampUpDone """ - SignalType - Represent the different types of signals the fast DAC can have. Valid values are `SINE`, `TRIANGLE` and `SAWTOOTH`. See [`signalTypeDAC`](@ref), [`signalTypeDAC!`](@ref). @@ -14,14 +39,24 @@ See [`signalTypeDAC`](@ref), [`signalTypeDAC!`](@ref). mutable struct ArbitraryWaveform <: AbstractArray{Float32, 1} samples::Vector{Float32} - ArbitraryWaveform(samples::Vector{Float32}) = length(samples) != _awgBufferSize ? error("Unexpected waveform length $(length(samples)), expected $_awgBufferSize") : new(samples) + function ArbitraryWaveform(samples::Vector{Float32}) + return if length(samples) != _awgBufferSize + error("Unexpected waveform length $(length(samples)), expected $_awgBufferSize") + else + new(samples) + end + end end Base.size(wave::ArbitraryWaveform) = size(wave.samples) Base.IndexStyle(::Type{<:ArbitraryWaveform}) = IndexLinear() Base.getindex(wave::ArbitraryWaveform, i::Int) = wave.samples[i] Base.setindex!(wave::ArbitraryWaveform, v, i::Int) = wave.samples[i] = v -ArbitraryWaveform(samples::Vector{T}) where T <: AbstractFloat = ArbitraryWaveform(convert(Vector{Float32}, samples)) -ArbitraryWaveform(f::Function, min=0, max=_awgBufferSize) = ArbitraryWaveform([f(x) for x = range(min, max, length = _awgBufferSize)]) +function ArbitraryWaveform(samples::Vector{T}) where {T <: AbstractFloat} + return ArbitraryWaveform(convert(Vector{Float32}, samples)) +end +function ArbitraryWaveform(f::Function, min = 0, max = _awgBufferSize) + return ArbitraryWaveform([f(x) for x ∈ range(min, max; length = _awgBufferSize)]) +end function waveform!_(rp::RedPitaya, channel::Integer, wave::ArbitraryWaveform) send(rp, "RP:DAC:CH$(channel-1):AWG") @@ -36,24 +71,29 @@ function waveformDAC!(rp::RedPitaya, channel::Integer, wave::ArbitraryWaveform) end return reply end -scaleWaveformDAC!(rp::RedPitaya, channel::Integer, scale::Float64) = waveform!_(rp, channel, ArbitraryWaveform(scale.*rp.awgs[:, channel])) -waveformDAC!(rp::RedPitaya, channel::Integer, samples::Vector{T}) where T <: AbstractFloat = waveformDAC!(rp, channel, ArbitraryWaveform(samples)) -waveformDAC!(rp::RedPitaya, channel::Integer, wave::Nothing) = waveformDAC!(rp, channel, ArbitraryWaveform(x-> Float32(0.0))) +function scaleWaveformDAC!(rp::RedPitaya, channel::Integer, scale::Float64) + return waveform!_(rp, channel, ArbitraryWaveform(scale .* rp.awgs[:, channel])) +end +function waveformDAC!(rp::RedPitaya, channel::Integer, samples::Vector{T}) where {T <: AbstractFloat} + return waveformDAC!(rp, channel, ArbitraryWaveform(samples)) +end +function waveformDAC!(rp::RedPitaya, channel::Integer, wave::Nothing) + return waveformDAC!(rp, channel, ArbitraryWaveform(x -> Float32(0.0))) +end function waveformDAC!(rp::RedPitaya, channel::Integer, signal::SignalType) wave = nothing if signal == SINE - wave = ArbitraryWaveform(x->sin(x), 0, 2*pi) + wave = ArbitraryWaveform(x -> sin(x), 0, 2 * pi) elseif TRIANGLE - wave = ArbitraryWaveform(x->2*abs(x/_awgBufferSize - floor(x/_awgBufferSize + 1/2))) + wave = ArbitraryWaveform(x -> 2 * abs(x / _awgBufferSize - floor(x / _awgBufferSize + 1 / 2))) elseif SAWTOOTH - wave = ArbitraryWaveform(x->2*(x/_awgBufferSize - 1/2)) + wave = ArbitraryWaveform(x -> 2 * (x / _awgBufferSize - 1 / 2)) else error("Signal type $(string(signal)) not implemented yet") end - waveformDAC!(rp, channel, wave) + return waveformDAC!(rp, channel, wave) end -normalize(wave::ArbitraryWaveform) = ArbitraryWaveform(wave/maximum(abs.(wave))) - +normalize(wave::ArbitraryWaveform) = ArbitraryWaveform(wave / maximum(abs.(wave))) struct DACPerformanceData uDeltaControl::UInt8 @@ -83,20 +123,24 @@ struct DACConfig signalTypes::Array{Union{String, Nothing}} jumpSharpness::Array{Union{Float64, Nothing}} function DACConfig() - new(Array{Union{Float64, Nothing}}(nothing, 2,4), Array{Union{Float64, Nothing}}(nothing, 2), - Array{Union{Float64, Nothing}}(nothing, 2,4), Array{Union{Float64, Nothing}}(nothing, 2,4), - Array{Union{String, Nothing}}(nothing, 2), Array{Union{Float64, Nothing}}(nothing, 2)) + return new( + Array{Union{Float64, Nothing}}(nothing, 2, 4), + Array{Union{Float64, Nothing}}(nothing, 2), + Array{Union{Float64, Nothing}}(nothing, 2, 4), + Array{Union{Float64, Nothing}}(nothing, 2, 4), + Array{Union{String, Nothing}}(nothing, 2), + Array{Union{Float64, Nothing}}(nothing, 2), + ) end end """ - amplitudeDAC(rp::RedPitaya, channel, component) - Return the amplitude of composite waveform `component` for `channel`. See [`amplitudeDAC!`](@ref). # Examples + ```julia julia> amplitudeDAC!(rp, 1, 1, 0.5); true @@ -109,17 +153,18 @@ function amplitudeDAC(rp::RedPitaya, channel, component) command = scpiCommand(amplitudeDAC, channel, component) return query(rp, command, Float64) end -scpiCommand(::typeof(amplitudeDAC), channel, component) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":AMP?") +function scpiCommand(::typeof(amplitudeDAC), channel, component) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":AMP?") +end scpiReturn(::typeof(amplitudeDAC)) = Float64 """ - amplitudeDAC!(rp::RedPitaya, channel, component, value) - Set the amplitude of composite waveform `component` for `channel`. Return `true` if the command was successful. See [`amplitudeDAC`](@ref). # Examples + ```julia julia> amplitudeDAC!(rp, 1, 1, 0.5); true @@ -135,17 +180,18 @@ function amplitudeDAC!(rp::RedPitaya, channel, component, value) command = scpiCommand(amplitudeDAC!, channel, component, value) return query(rp, command, Bool) end -scpiCommand(::typeof(amplitudeDAC!), channel, component, value) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":AMP ", Float64(value)) +function scpiCommand(::typeof(amplitudeDAC!), channel, component, value) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":AMP ", Float64(value)) +end scpiReturn(::typeof(amplitudeDAC!)) = Bool """ - offsetDAC(rp::RedPitaya, channel) - Return the offset for `channel`. See [`offsetDAC!`](@ref). # Examples + ```julia julia> offsetDAC!(rp, 1, 0.2); true @@ -158,16 +204,15 @@ function offsetDAC(rp::RedPitaya, channel) command = scpiCommand(offsetDAC, channel) return query(rp, command, Float64) end -scpiCommand(::typeof(offsetDAC), channel) = string("RP:DAC:CH", Int(channel)-1, ":OFF?") +scpiCommand(::typeof(offsetDAC), channel) = string("RP:DAC:CH", Int(channel) - 1, ":OFF?") scpiReturn(::typeof(offsetDAC)) = Float64 """ - offsetDAC!(rp::RedPitaya, channel, value) - Set the offset for `channel`. Return `true` if the command was successful. See [`offsetDAC`](@ref). # Examples + ```julia julia> offsetDAC!(rp, 1, 0.2); true @@ -183,7 +228,9 @@ function offsetDAC!(rp::RedPitaya, channel, value) command = scpiCommand(offsetDAC!, channel, value) return query(rp, command, Bool) end -scpiCommand(::typeof(offsetDAC!), channel, value) = string("RP:DAC:CH", Int(channel)-1, ":OFF ", Float64(value)) +function scpiCommand(::typeof(offsetDAC!), channel, value) + return string("RP:DAC:CH", Int(channel) - 1, ":OFF ", Float64(value)) +end scpiReturn(::typeof(offsetDAC!)) = Bool function offsetDACSeq!(rp::RedPitaya, channel, value) if value > 1.0 @@ -192,23 +239,24 @@ function offsetDACSeq!(rp::RedPitaya, channel, value) command = scpiCommand(offsetDACSeq!, channel, value) return query(rp, command, Bool) end -scpiCommand(::typeof(offsetDACSeq!), channel, value) = string("RP:DAC:SEQ:CH", Int(channel)-1, ":OFF ", Float64(value)) +function scpiCommand(::typeof(offsetDACSeq!), channel, value) + return string("RP:DAC:SEQ:CH", Int(channel) - 1, ":OFF ", Float64(value)) +end scpiReturn(::typeof(offsetDACSeq!)) = Bool function offsetDACSeq!(config::DACConfig, channel, value) if value > 1.0 error("$value is larger than 1.0 V!") end - config.offsets[channel] = value + return config.offsets[channel] = value end """ - frequencyDAC(rp::RedPitaya, channel, component) - Return the frequency of composite waveform `component` for `channel`. See [`frequencyDAC!`](@ref). # Examples + ```julia julia> frequencyDAC!(rp, 1, 1, 2400); true @@ -221,16 +269,17 @@ function frequencyDAC(rp::RedPitaya, channel, component) command = scpiCommand(frequencyDAC, channel, component) return query(rp, command, Float64) end -scpiCommand(::typeof(frequencyDAC), channel, component) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":FREQ?") +function scpiCommand(::typeof(frequencyDAC), channel, component) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":FREQ?") +end scpiReturn(::typeof(frequencyDAC)) = Float64 """ - frequencyDAC!(rp::RedPitaya, channel, component, value) - Set the frequency of composite waveform `component` for `channel`. Return `true` if the command was successful. See [`frequencyDAC`](@ref). # Examples + ```julia julia> frequencyDAC!(rp, 1, 1, 2400); true @@ -243,27 +292,30 @@ function frequencyDAC!(rp::RedPitaya, channel, component, value) command = scpiCommand(frequencyDAC!, channel, component, value) return query(rp, command, Bool) end -scpiCommand(::typeof(frequencyDAC!), channel, component, value) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":FREQ ", Float64(value)) +function scpiCommand(::typeof(frequencyDAC!), channel, component, value) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":FREQ ", Float64(value)) +end scpiReturn(::typeof(frequencyDAC!)) = Bool function frequencyDACSeq!(rp::RedPitaya, channel, component, value) command = scpiCommand(frequencyDACSeq!, channel, component, value) return query(rp, command, Bool) end -scpiCommand(::typeof(frequencyDACSeq!), channel, component, value) = string("RP:DAC:SEQ:CH", Int(channel)-1, ":COMP", Int(component)-1, ":FREQ ", Float64(value)) +function scpiCommand(::typeof(frequencyDACSeq!), channel, component, value) + return string("RP:DAC:SEQ:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":FREQ ", Float64(value)) +end scpiReturn(::typeof(frequencyDACSeq!)) = Bool function frequencyDACSeq!(config::DACConfig, channel, component, value) - config.frequencies[channel, component] = value + return config.frequencies[channel, component] = value end """ - phaseDAC(rp::RedPitaya, channel, component) - Return the phase of composite waveform `component` for `channel`. See [`phaseDAC!`](@ref). # Examples + ```julia julia> phaseDAC!(rp, 1, 1, 0.0); true @@ -276,16 +328,17 @@ function phaseDAC(rp::RedPitaya, channel, component) command = scpiCommand(phaseDAC, channel, component) return query(rp, command, scpiReturn(phaseDAC)) end -scpiCommand(::typeof(phaseDAC), channel, component) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":PHA?") +function scpiCommand(::typeof(phaseDAC), channel, component) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":PHA?") +end scpiReturn(::typeof(phaseDAC)) = Float64 """ - phaseDAC!(rp::RedPitaya, channel, component, value) - Set the phase of composite waveform `component` for `channel`. Return `true` if the command was successful. See [`phaseDAC`](@ref). # Examples + ```julia julia> phaseDAC!(rp, 1, 1, 0.0); true @@ -298,27 +351,28 @@ function phaseDAC!(rp::RedPitaya, channel, component, value) command = scpiCommand(phaseDAC!, channel, component, value) return query(rp, command, Bool) end -scpiCommand(::typeof(phaseDAC!), channel, component, value) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":PHA ", Float64(value)) -scpiReturn(::typeof(phaseDAC!)) = Bool +function scpiCommand(::typeof(phaseDAC!), channel, component, value) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":PHA ", Float64(value)) +end +scpiReturn(::typeof(phaseDAC!)) = Bool function phaseDACSeq!(rp::RedPitaya, channel, component, value) command = scpiCommand(phaseDACSeq!, channel, component, value) return query(rp, command, Bool) end -scpiCommand(::typeof(phaseDACSeq!), channel, component, value) = string("RP:DAC:SEQ:CH", Int(channel)-1, ":COMP", Int(component)-1, ":PHA ", Float64(value)) -scpiReturn(::typeof(phaseDACSeq!)) = Bool -function phaseDACSeq!(config::DACConfig, channel, component, value) - config.phases[channel, component] = value +function scpiCommand(::typeof(phaseDACSeq!), channel, component, value) + return string("RP:DAC:SEQ:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":PHA ", Float64(value)) end +scpiReturn(::typeof(phaseDACSeq!)) = Bool +phaseDACSeq!(config::DACConfig, channel, component, value) = config.phases[channel, component] = value """ - signalTypeDAC!(rp::RedPitaya, channel, value) - Return the signalType of composite waveform for `channel`. See [`signalTypeDAC!`](@ref). # Examples + ```julia julia> signalTypeDAC!(rp, 1, SINE); true @@ -331,22 +385,22 @@ function signalTypeDAC(rp::RedPitaya, channel, component) command = scpiCommand(signalTypeDAC, channel, component) return stringToEnum(SignalType, strip(query(rp, command), '\"')) end -scpiCommand(::typeof(signalTypeDAC), channel, component) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":SIGnaltype?") +function scpiCommand(::typeof(signalTypeDAC), channel, component) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":SIGnaltype?") +end scpiReturn(::typeof(signalTypeDAC)) = SignalType parseReturn(::typeof(signalTypeDAC), ret) = stringToEnum(SignalType, strip(ret, '\"')) - function signalTypeDAC!(rp::RedPitaya, channel, component, sigType::String) return signalTypeDAC!(rp, channel, component, stringToEnum(SignalType, sigType)) end """ - signalTypeDAC!(rp::RedPitaya, channel, value) - Set the signalType of composite waveform for `channel`. Return `true` if the command was successful. See [`signalTypeDAC`](@ref). # Examples + ```julia julia> signalTypeDAC!(rp, 1, SINE); true @@ -358,71 +412,91 @@ SINE function signalTypeDAC!(rp::RedPitaya, channel, component, sigType::SignalType) return query(rp, scpiCommand(signalTypeDAC!, channel, component, sigType), scpiReturn(signalTypeDAC!)) end -scpiCommand(::typeof(signalTypeDAC!), channel, component, sigType) = string("RP:DAC:CH", Int(channel)-1, ":COMP", Int(component)-1, ":SIGnaltype ", string(sigType)) +function scpiCommand(::typeof(signalTypeDAC!), channel, component, sigType) + return string("RP:DAC:CH", Int(channel) - 1, ":COMP", Int(component) - 1, ":SIGnaltype ", string(sigType)) +end scpiReturn(::typeof(signalTypeDAC!)) = Bool - function rampingDAC!(rp::RedPitaya, channel, value) command = scpiCommand(rampingDAC!, channel, value) return query(rp, command, scpiReturn(rampingDAC!)) end -scpiCommand(::typeof(rampingDAC!), channel, value) = string("RP:DAC:CH", Int(channel)-1, ":RAMP ", Float64(value)) +function scpiCommand(::typeof(rampingDAC!), channel, value) + return string("RP:DAC:CH", Int(channel) - 1, ":RAMP ", Float64(value)) +end scpiReturn(::typeof(rampingDAC!)) = Bool function rampingDAC(rp::RedPitaya, channel) command = scpiCommand(rampingDAC, channel) return query(rp, command, scpiReturn(rampingDAC)) end -scpiCommand(::typeof(rampingDAC), channel) = string("RP:DAC:CH", Int(channel)-1, ":RAMP?") +scpiCommand(::typeof(rampingDAC), channel) = string("RP:DAC:CH", Int(channel) - 1, ":RAMP?") scpiReturn(::typeof(rampingDAC)) = Float64 function enableRamping!(rp::RedPitaya, channel, value) return query(rp, scpiCommand(enableRamping!, channel, value), scpiReturn(enableRamping!)) end -scpiCommand(::typeof(enableRamping!), channel, val::Bool) = scpiCommand(enableRamping!, channel, val ? "ON" : "OFF") -scpiCommand(::typeof(enableRamping!), channel, val::String) = string("RP:DAC:CH", Int(channel)-1, ":RAMPing:ENaBle ", val) +function scpiCommand(::typeof(enableRamping!), channel, val::Bool) + return scpiCommand(enableRamping!, channel, val ? "ON" : "OFF") +end +function scpiCommand(::typeof(enableRamping!), channel, val::String) + return string("RP:DAC:CH", Int(channel) - 1, ":RAMPing:ENaBle ", val) +end scpiReturn(::typeof(enableRamping!)) = Bool enableRamping(rp::RedPitaya, channel) = occursin("ON", query(rp, scpiCommand(enableRamping, channel))) -scpiCommand(::typeof(enableRamping), channel) = string("RP:DAC:CH", Int(channel)-1, ":RAMPing:ENaBle?") +scpiCommand(::typeof(enableRamping), channel) = string("RP:DAC:CH", Int(channel) - 1, ":RAMPing:ENaBle?") scpiReturn(::typeof(enableRamping)) = String parseReturn(::typeof(enableRamping), ret) = occursin("ON", ret) function enableRampDown!(rp::RedPitaya, channel, value) return query(rp, scpiCommand(enableRampDown!, channel, value), scpiReturn(enableRampDown!)) end -scpiCommand(::typeof(enableRampDown!), channel, val::Bool) = scpiCommand(enableRampDown!, channel, val ? "ON" : "OFF") -scpiCommand(::typeof(enableRampDown!), channel, val::String) = string("RP:DAC:CH", Int(channel)-1, ":RAMPing:DoWN ", val) +function scpiCommand(::typeof(enableRampDown!), channel, val::Bool) + return scpiCommand(enableRampDown!, channel, val ? "ON" : "OFF") +end +function scpiCommand(::typeof(enableRampDown!), channel, val::String) + return string("RP:DAC:CH", Int(channel) - 1, ":RAMPing:DoWN ", val) +end scpiReturn(::typeof(enableRampDown!)) = Bool enableRampDown(rp::RedPitaya, channel) = occursin("ON", query(rp, scpiCommand(enableRampDown, channel))) -scpiCommand(::typeof(enableRampDown), channel) = string("RP:DAC:CH", Int(channel)-1, ":RAMPing:DoWN?") +scpiCommand(::typeof(enableRampDown), channel) = string("RP:DAC:CH", Int(channel) - 1, ":RAMPing:DoWN?") scpiReturn(::typeof(enableRampDown)) = String parseReturn(::typeof(enableRampDown), ret) = occursin("ON", ret) function rampingStatus(rp::RedPitaya) status = query(rp, scpiCommand(rampingStatus), scpiReturn(rampingStatus)) - result = RampingStatus(status & 1, (status >> 4) & 1, - RampingState((status >> 1) & 0x7), RampingState((status >> 5) & 0x7)) + result = RampingStatus( + status & 1, + (status >> 4) & 1, + RampingState((status >> 1) & 0x7), + RampingState((status >> 5) & 0x7), + ) return result end scpiCommand(::typeof(rampingStatus)) = "RP:DAC:RAMPing:STATus?" scpiReturn(::typeof(rampingStatus)) = UInt8 function parseReturn(::typeof(rampingStatus), ret) status = parse(UInt8, ret) - result = RampingStatus(status & 1, (status >> 4) & 1, - RampingState((status >> 1) & 0x7), RampingState((status >> 5) & 0x7)) + result = RampingStatus( + status & 1, + (status >> 4) & 1, + RampingState((status >> 1) & 0x7), + RampingState((status >> 5) & 0x7), + ) return result end function rampDownDone(rp::RedPitaya) done = false status = rampingStatus(rp) - done = (!status.enableCh1 || status.stateCh1 == DONE) && (!status.enableCh2 || status.stateCh2 == DONE) + return done = + (!status.enableCh1 || status.stateCh1 == DONE) && (!status.enableCh2 || status.stateCh2 == DONE) end function rampUpDone(rp::RedPitaya) done = false status = rampingStatus(rp) - done = (!status.enableCh1 || status.stateCh1 != UP) && (!status.enableCh2 || status.stateCh2 != UP) -end \ No newline at end of file + return done = (!status.enableCh1 || status.stateCh1 != UP) && (!status.enableCh2 || status.stateCh2 != UP) +end diff --git a/src/client/julia/src/EEPROM.jl b/src/client/julia/src/EEPROM.jl index 8ae6ec08..84d3fb70 100644 --- a/src/client/julia/src/EEPROM.jl +++ b/src/client/julia/src/EEPROM.jl @@ -1,10 +1,8 @@ export calibDACOffset, calibDACOffset!, calibADCScale, calibADCScale!, calibADCOffset, calibADCOffset!, updateCalib!, calibDACScale!, calibDACScale, calibFlags, calibDACLowerLimit, calibDACLowerLimit!, calibDACUpperLimit, calibDACUpperLimit!, calibDACLimit! """ - calibDACOffset!(rp::RedPitaya, channel::Integer, val) - -Store calibration DAC offset `val` for given channel into the RedPitayas EEPROM. -This value is used by the server to offset the output voltage. Absolute value has to be smaller than 1.0 V. +Store calibration DAC offset `val` for given channel into the RedPitayas EEPROM. +This value is used by the server to offset the output voltage. Absolute value has to be smaller than 1.0 V. """ function calibDACOffset!(rp::RedPitaya, channel::Integer, val) if abs(val) > 1.0 @@ -12,42 +10,42 @@ function calibDACOffset!(rp::RedPitaya, channel::Integer, val) end return query(rp, scpiCommand(calibDACOffset!, channel, val), scpiReturn(calibDACOffset!)) end -scpiCommand(::typeof(calibDACOffset!), channel::Integer, val) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":OFF $(Float32(val))") +function scpiCommand(::typeof(calibDACOffset!), channel::Integer, val) + return string("RP:CALib:DAC:CH", Int(channel) - 1, ":OFF $(Float32(val))") +end scpiReturn(::typeof(calibDACOffset!)) = Bool """ - calibDACOffset(rp::RedPitaya, channel::Integer) - -Retrieve the calibration DAC offset for given channel from the RedPitayas EEPROM +Retrieve the calibration DAC offset for given channel from the RedPitayas EEPROM """ -calibDACOffset(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibDACOffset, channel), scpiReturn(calibDACOffset)) +function calibDACOffset(rp::RedPitaya, channel::Integer) + return query(rp, scpiCommand(calibDACOffset, channel), scpiReturn(calibDACOffset)) +end scpiCommand(::typeof(calibDACOffset), channel::Integer) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":OFF?") scpiReturn(::typeof(calibDACOffset)) = Float64 """ - calibDACScale(rp::RedPitaya, channel::Integer) - Store calibration DAC scale `val` for given channel into the RedPitayas EEPROM. This value is used by the server to scale the output voltage. """ function calibDACScale!(rp::RedPitaya, channel::Integer, val) return query(rp, scpiCommand(calibDACScale!, channel, val), scpiReturn(calibDACScale!)) end -scpiCommand(::typeof(calibDACScale!), channel::Integer, val) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":SCA $(Float32(val))") +function scpiCommand(::typeof(calibDACScale!), channel::Integer, val) + return string("RP:CALib:DAC:CH", Int(channel) - 1, ":SCA $(Float32(val))") +end scpiReturn(::typeof(calibDACScale!)) = Bool """ - calibDACScale(rp::RedPitaya, channel::Integer) - Retrieve the calibration DAC scale for given channel from the RedPitayas EEPROM. """ -calibDACScale(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibDACScale, channel), scpiReturn(calibDACScale)) +function calibDACScale(rp::RedPitaya, channel::Integer) + return query(rp, scpiCommand(calibDACScale, channel), scpiReturn(calibDACScale)) +end scpiCommand(::typeof(calibDACScale), channel::Integer) = string("RP:CALib:DAC:CH", Int(channel) - 1, ":SCA?") scpiReturn(::typeof(calibDACScale)) = Float64 """ - calibDACLowerLimit!(rp::RedPitaya, channel::Integer) - Store calibration DAC lower limit `val` for given channel into the RedPitayas EEPROM. This value is used by the server to limit the output voltage. """ @@ -58,8 +56,6 @@ scpiCommand(::typeof(calibDACLowerLimit!), channel::Integer, val) = string("RP:C scpiReturn(::typeof(calibDACLowerLimit!)) = Bool """ - calibDACLowerLimit(rp::RedPitaya, channel::Integer) - Retrieve the calibration DAC lower limit for given channel from the RedPitayas EEPROM. """ calibDACLowerLimit(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibDACLowerLimit, channel), scpiReturn(calibDACLowerLimit)) @@ -67,8 +63,6 @@ scpiCommand(::typeof(calibDACLowerLimit), channel::Integer) = string("RP:CALib:D scpiReturn(::typeof(calibDACLowerLimit)) = Float64 """ - calibDACUpperLimit!(rp::RedPitaya, channel::Integer) - Store calibration DAC upper limit `val` for given channel into the RedPitayas EEPROM. This value is used by the server to limit the output voltage. """ @@ -79,8 +73,6 @@ scpiCommand(::typeof(calibDACUpperLimit!), channel::Integer, val) = string("RP:C scpiReturn(::typeof(calibDACUpperLimit!)) = Bool """ - calibDACUpperLimit(rp::RedPitaya, channel::Integer) - Retrieve the calibration DAC upper limit for given channel from the RedPitayas EEPROM. """ calibDACUpperLimit(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibDACUpperLimit, channel), scpiReturn(calibDACUpperLimit)) @@ -88,8 +80,6 @@ scpiCommand(::typeof(calibDACUpperLimit), channel::Integer) = string("RP:CALib:D scpiReturn(::typeof(calibDACUpperLimit)) = Float64 """ - calibDACLimit!(rp::RedPitaya, channel, val) - Applies `val` with a positive sign as the upper and with a negative sign as the lower calibration DAC limit. See also [calibDACUpperLimit!](@ref), [calibDACLowerLimit!](@ref) @@ -102,8 +92,6 @@ function calibDACLimit!(rp::RedPitaya, channel::Integer, val) end """ - calibADCOffset!(rp::RedPitaya, channel::Integer, val) - Store calibration ADC offset `val` for given channel into the RedPitayas EEPROM. Absolute value has to be smaller than 1.0 V. @@ -116,23 +104,23 @@ function calibADCOffset!(rp::RedPitaya, channel::Integer, val) rp.calib[2, channel] = Float32(val) return query(rp, scpiCommand(calibADCOffset!, channel, val), scpiReturn(calibADCOffset!)) end -scpiCommand(::typeof(calibADCOffset!), channel::Integer, val) = string("RP:CALib:ADC:CH", Int(channel) - 1, ":OFF $(Float32(val))") +function scpiCommand(::typeof(calibADCOffset!), channel::Integer, val) + return string("RP:CALib:ADC:CH", Int(channel) - 1, ":OFF $(Float32(val))") +end scpiReturn(::typeof(calibADCOffset!)) = Bool """ - calibADCOffset(rp::RedPitaya, channel::Integer) - Retrieve the calibration ADC offset for given channel from the RedPitayas EEPROM. See also [convertSamplesToPeriods!](@ref),[convertSamplesToFrames](@ref). """ -calibADCOffset(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibADCOffset, channel), scpiReturn(calibADCOffset)) +function calibADCOffset(rp::RedPitaya, channel::Integer) + return query(rp, scpiCommand(calibADCOffset, channel), scpiReturn(calibADCOffset)) +end scpiCommand(::typeof(calibADCOffset), channel::Integer) = string("RP:CALib:ADC:CH", Int(channel) - 1, ":OFF?") scpiReturn(::typeof(calibADCOffset)) = Float64 """ - calibADCScale(rp::RedPitaya, channel::Integer) - Store calibration ADC scale `val` for given channel into the RedPitayas EEPROM. See also [convertSamplesToPeriods!](@ref),[convertSamplesToFrames](@ref). """ @@ -140,17 +128,19 @@ function calibADCScale!(rp::RedPitaya, channel::Integer, val) rp.calib[1, channel] = Float32(val) return query(rp, scpiCommand(calibADCScale!, channel::Integer, val), scpiReturn(calibADCScale!)) end -scpiCommand(::typeof(calibADCScale!), channel, val) = string("RP:CALib:ADC:CH", Int(channel) - 1, ":SCA $(Float32(val))") +function scpiCommand(::typeof(calibADCScale!), channel, val) + return string("RP:CALib:ADC:CH", Int(channel) - 1, ":SCA $(Float32(val))") +end scpiReturn(::typeof(calibADCScale!)) = Bool """ - calibADCScale(rp::RedPitaya, channel::Integer) - Retrieve the calibration ADC scale for given channel from the RedPitayas EEPROM. See also [convertSamplesToPeriods!](@ref),[convertSamplesToFrames](@ref). """ -calibADCScale(rp::RedPitaya, channel::Integer) = query(rp, scpiCommand(calibADCScale, channel), scpiReturn(calibADCScale)) +function calibADCScale(rp::RedPitaya, channel::Integer) + return query(rp, scpiCommand(calibADCScale, channel), scpiReturn(calibADCScale)) +end scpiCommand(::typeof(calibADCScale), channel::Integer) = string("RP:CALib:ADC:CH", Int(channel) - 1, ":SCA?") scpiReturn(::typeof(calibADCScale)) = Float64 @@ -159,8 +149,6 @@ scpiCommand(::typeof(calibFlags)) = "RP:CALib:FLAGs" scpiReturn(::typeof(calibFlags)) = Int64 """ - updateCalib!(rp::RedPitaya) - Update the cached calibration values. See also [calibADCScale](@ref), [calibADCOffset](@ref). @@ -169,5 +157,5 @@ function updateCalib!(rp::RedPitaya) rp.calib[1, 1] = calibADCScale(rp, 1) rp.calib[2, 1] = calibADCOffset(rp, 1) rp.calib[1, 2] = calibADCScale(rp, 2) - rp.calib[2, 2] = calibADCOffset(rp, 2) -end \ No newline at end of file + return rp.calib[2, 2] = calibADCOffset(rp, 2) +end diff --git a/src/client/julia/src/RedPitayaDAQServer.jl b/src/client/julia/src/RedPitayaDAQServer.jl index c9f9b819..3e8ea76d 100644 --- a/src/client/julia/src/RedPitayaDAQServer.jl +++ b/src/client/julia/src/RedPitayaDAQServer.jl @@ -1,7 +1,7 @@ module RedPitayaDAQServer # package code goes here - +using DocStringExtensions using Base: UInt16 using Sockets import Sockets: send, connect @@ -12,7 +12,22 @@ import LinearAlgebra: normalize import Base: reset, iterate, length, push!, pop! -export RedPitaya, send, receive, query, disconnect, ServerMode, serverMode, serverMode!, CONFIGURATION, ACQUISITION, TRANSMISSION, getLog, ScpiBatch, execute!, clear!, @add_batch +export RedPitaya, + send, + receive, + query, + disconnect, + ServerMode, + serverMode, + serverMode!, + CONFIGURATION, + ACQUISITION, + TRANSMISSION, + getLog, + ScpiBatch, + execute!, + clear!, + @add_batch # TODO: The update stuff increases load times quite a bit. Should we move this to a script? # using ProgressMeter @@ -22,15 +37,27 @@ export RedPitaya, send, receive, query, disconnect, ServerMode, serverMode, serv # using URIs # using ZipFile +# Configure templates for docstrings +@template TYPES = """ + $(TYPEDEF) + $(DOCSTRING) + + # Fields + $(TYPEDFIELDS) + """ + +@template (FUNCTIONS, METHODS, MACROS) = """ + $(TYPEDSIGNATURES) + $(DOCSTRING) + """ + const _awgBufferSize = 16384 """ - RedPitaya - Struct representing a connection to a RedPitayaDAQServer. -Contains the sockets used for communication and connection related metadata. Also contains fields for -client specific concepts such as periods, frames and calibration values. +Contains the sockets used for communication and connection related metadata. Also contains fields for +client specific concepts such as periods, frames and calibration values. """ mutable struct RedPitaya host::String @@ -52,33 +79,25 @@ end # Iterable Interface length(rp::RedPitaya) = 1 -iterate(rp::RedPitaya, state=1) = state > 1 ? nothing : (rp, state + 1) +iterate(rp::RedPitaya, state = 1) = state > 1 ? nothing : (rp, state + 1) """ - send(rp::RedPitaya, cmd::String) - Send a command to the RedPitaya. Appends delimiter. """ -function send(rp::RedPitaya,cmd::String) +function send(rp::RedPitaya, cmd::String) @debug "RP $(rp.host) sent: $cmd" - write(rp.socket,cmd*rp.delim) + return write(rp.socket, cmd * rp.delim) end const _timeout = Ref(5.0) const _scaleWarning = 0.1 """ - receive(rp::RedPitaya) - Receive a String from the RedPitaya command socket. Reads until a whole line is received """ -function receive(rp::RedPitaya) - return readline(rp.socket)[1:end] -end +receive(rp::RedPitaya) = readline(rp.socket)[1:end] """ - receive(rp::RedPitaya, ch::Channel) - Receive a String from the RedPitaya command socket. Reads until a whole line is received and puts it in the supplied channel `ch`. """ function receive(rp::RedPitaya, ch::Channel) @@ -86,8 +105,6 @@ function receive(rp::RedPitaya, ch::Channel) end """ - receive(rp::RedPitaya, timeout::Number) - Receive a string from the RedPitaya command socket. Reads until a whole line is received or timeout seconds passed. In the latter case an error is thrown. """ @@ -96,7 +113,7 @@ function receive(rp::RedPitaya, timeout::Number) t = @async receive(rp, ch) result = nothing timeoutTimer = Timer(_ -> close(ch), timeout) - try + try result = take!(ch) catch e @async Base.throwto(t, EOFError()) @@ -113,31 +130,27 @@ function receive(rp::RedPitaya, timeout::Number) end """ - query(rp::RedPitaya, cmd [, timeout = 5.0, N = 100]) - Send a query to the RedPitaya command socket. Return reply as String. Waits for `timeout` seconds and checks every `timeout/N` seconds. See also [receive](@ref). """ -function query(rp::RedPitaya, cmd::String, timeout::Number=getTimeout()) +function query(rp::RedPitaya, cmd::String, timeout::Number = getTimeout()) send(rp, cmd) Sockets.quickack(rp.socket, true) - receive(rp, timeout) + return receive(rp, timeout) end """ - query(rp::RedPitaya, cmd, T::Type [timeout = 5.0, N = 100]) - Send a query to the RedPitaya. Parse reply as `T`. Waits for `timeout` seconds and checks every `timeout/N` seconds. """ -function query(rp::RedPitaya, cmd::String, T::Type, timeout::Number=getTimeout()) +function query(rp::RedPitaya, cmd::String, T::Type, timeout::Number = getTimeout()) a = query(rp, cmd, timeout) if T == String - return a[2:end-1] # Strings are wrapped like this: "\"OUT\"" + return a[2:(end - 1)] # Strings are wrapped like this: "\"OUT\"" else parse(T, a) end @@ -201,7 +214,7 @@ function getLog(rp::RedPitaya, log) recv = recv + length(buff) write(log, buff) end - close(log) + return close(log) end function stringToEnum(enumType::Type{T}, value::AbstractString) where {T <: Enum} @@ -209,7 +222,12 @@ function stringToEnum(enumType::Type{T}, value::AbstractString) where {T <: Enum # If lowercase is not sufficient one could try Unicode.normalize with casefolding index = findfirst(isequal(lowercase(value)), lowercase.(stringInstances)) if isnothing(index) - throw(ArgumentError("$value cannot be resolved to an instance of $(enumType). Possible instances are: " * join(stringInstances, ", ", " and "))) + throw( + ArgumentError( + "$value cannot be resolved to an instance of $(enumType). Possible instances are: " * + join(stringInstances, ", ", " and "), + ), + ) end return instances(enumType)[index] end @@ -219,8 +237,6 @@ scpiCommand(::typeof(imgversion)) = "RP:VERsion:IMAGe?" scpiReturn(::typeof(imgversion)) = UInt32 """ - ServerMode - Represent the different modes the server can be in. Valid values are `CONFIGURATION`, `ACQUISITION` and `TRANSMISSION`. See also [`serverMode`](@ref), [`serverMode!`](@ref). @@ -228,11 +244,10 @@ See also [`serverMode`](@ref), [`serverMode!`](@ref). @enum ServerMode CONFIGURATION ACQUISITION TRANSMISSION """ - serverMode(rp::RedPitaya) - Return the mode of the server. # Examples + ```julia julia> serverMode!(rp, ACQUISITION); true @@ -241,19 +256,16 @@ julia> serverMode(rp) ACQUISITION ``` """ -function serverMode(rp::RedPitaya) - return stringToEnum(ServerMode, strip(query(rp, scpiCommand(serverMode)), '\"')) -end +serverMode(rp::RedPitaya) = stringToEnum(ServerMode, strip(query(rp, scpiCommand(serverMode)), '\"')) scpiCommand(::typeof(serverMode)) = "RP:MODe?" scpiReturn(::typeof(serverMode)) = ServerMode parseReturn(::typeof(serverMode), ret) = stringToEnum(ServerMode, strip(ret, '\"')) """ - serverMode!(rp::RedPitaya, mode::ServerMode) - Set the mode of the server. Valid values are "`CONFIGURATION`" and "`ACQUISITION`". # Examples + ```julia julia> serverMode!(rp, ACQUISITION); true @@ -262,15 +274,12 @@ julia> serverMode(rp) ACQUISITION ``` """ -function serverMode!(rp::RedPitaya, mode::String) - return serverMode!(rp, stringToEnum(ServerMode, mode)) -end +serverMode!(rp::RedPitaya, mode::String) = serverMode!(rp, stringToEnum(ServerMode, mode)) """ - serverMode!(rp::RedPitaya, mode::ServerMode) - Set the mode of the server. # Examples + ```julia julia> serverMode!(rp, ACQUISITION); true @@ -285,10 +294,7 @@ end scpiCommand(::typeof(serverMode!), mode) = string("RP:MODe ", string(mode)) scpiReturn(::typeof(serverMode!)) = Bool - """ - ScpiBatch - Struct representing a batch of SCPI commands for a RedPitaya. Only commands that interact exclusively with the command socket should be used in a batch. """ struct ScpiBatch @@ -297,48 +303,44 @@ end ScpiBatch() = ScpiBatch([]) """ - push!(batch::ScpiBatch, cmd::Pair{K, T}) where {K<:Function, T<:Tuple} - Add the given function and arguments to the batch + # Examples + ```julia -julia> batch = ScpiBatch() +julia> batch = ScpiBatch() julia> push!(batch, amplitudeDAC! => (1, 1, 0.2)) -``` -""" -push!(batch::ScpiBatch, cmd::Pair{K, T}) where {K<:Function, T<:Tuple} = push!(batch.cmds, cmd) -push!(batch::ScpiBatch, cmd::Pair{K, T}) where {K<:Function, T<:Any} = push!(batch, cmd.first => (cmd.second,)) + +``` """ - pop!(batch::ScpiBatch) +push!(batch::ScpiBatch, cmd::Pair{K, T}) where {K <: Function, T <: Tuple} = push!(batch.cmds, cmd) +function push!(batch::ScpiBatch, cmd::Pair{K, T}) where {K <: Function, T <: Any} + return push!(batch, cmd.first => (cmd.second,)) +end +""" Remove the last added command from the batch """ pop!(batch::ScpiBatch) = pop!(batch.cmds) """ - clear!(batch::ScpiBatch) - Remove all commands from the batch """ -function clear!(batch::ScpiBatch) - batch.cmds = [] -end +clear!(batch::ScpiBatch) = batch.cmds = [] """ - execute!(rp::RedPitaya, batch::ScpiBatch) - Executes all commands of the given batch. Returns an array of the results in the order of the commands. An element is `nothing` if the command has no return value. """ function execute!(rp::RedPitaya, batch::ScpiBatch) # Send everything first - cmds = join([scpiCommand(f, args...) for (f, args) in batch.cmds], rp.delim) + cmds = join([scpiCommand(f, args...) for (f, args) ∈ batch.cmds], rp.delim) send(rp, cmds) # Then receive Sockets.quickack(rp.socket, true) result = Array{Any}(nothing, length(batch.cmds)) - for (i, (f, _)) in enumerate(batch.cmds) + for (i, (f, _)) ∈ enumerate(batch.cmds) if !isnothing(scpiReturn(f)) ret = receive(rp, getTimeout()) Sockets.quickack(rp.socket, true) # quickack can be reset after tcp operations @@ -349,17 +351,17 @@ function execute!(rp::RedPitaya, batch::ScpiBatch) end """ - @add_batch batch cmd - Append a usual RedPitaya function to the given batch instead of evaluating it directly. See also [`ScpiBatch`](@ref), [`push!`](@ref), [`execute!`](@ref) # Examples + ```julia -julia> execute!(rp) do b - @add_batch b serverMode!(rp, CONFIGURATION) - end +julia> execute!(rp) do b + @add_batch b serverMode!(rp, CONFIGURATION) + end + ``` """ macro add_batch(batch::Symbol, expr::Expr) @@ -369,13 +371,12 @@ macro add_batch(batch::Symbol, expr::Expr) func = expr.args[1] tuple = Expr(:call) push!(tuple.args, :tuple) - for arg in expr.args[3:end] + for arg ∈ expr.args[3:end] push!(tuple.args, :($(esc(arg)))) end return :(push!($(esc(batch)), $func => $tuple)) end - include("DAC.jl") include("Sequence.jl") include("ADC.jl") @@ -389,12 +390,10 @@ include("CounterTrigger.jl") function destroy(rp::RedPitaya) disconnect(rp) - rp.destroyed = true + return rp.destroyed = true end """ - RedPitaya(ip [, port = 5025, dataPort=5026, isMaster = false]) - Construct a `RedPitaya`. During the construction the connection @@ -402,6 +401,7 @@ is established and the calibration values are loaded from the RedPitayas EEPROM. Throws an error if a timeout occurs while attempting to connect. # Examples + ```julia julia> rp = RedPitaya("192.168.1.100"); @@ -412,9 +412,23 @@ julia> decimation(rp) 8 ``` """ -function RedPitaya(host::String, port::Int64=5025, dataPort::Int64=5026, isMaster::Bool=true) - - rp = RedPitaya(host, port, dataPort, "\n", TCPSocket(), TCPSocket(), 1, 1, 1, false, isMaster, false, zeros(Float32, 2, 2), zeros(Float32, _awgBufferSize, 2)) +function RedPitaya(host::String, port::Int64 = 5025, dataPort::Int64 = 5026, isMaster::Bool = true) + rp = RedPitaya( + host, + port, + dataPort, + "\n", + TCPSocket(), + TCPSocket(), + 1, + 1, + 1, + false, + isMaster, + false, + zeros(Float32, 2, 2), + zeros(Float32, _awgBufferSize, 2), + ) connect(rp) @@ -428,15 +442,14 @@ parseReturn(f::Function, ret) = parse(scpiReturn(f), ret) export setTimeout """ - Set the global timeout used in all functions of the package +Set the global timeout used in all functions of the package """ -setTimeout(_timeoutParam::T) where T <: Real = global _timeout[] = _timeoutParam +setTimeout(_timeoutParam::T) where {T <: Real} = global _timeout[] = _timeoutParam export getTimeout """ - Get the global timeout used in all functions of the package +Get the global timeout used in all functions of the package """ getTimeout() = _timeout[] - end # module diff --git a/src/client/julia/src/Sequence.jl b/src/client/julia/src/Sequence.jl index db223879..e89b08cf 100644 --- a/src/client/julia/src/Sequence.jl +++ b/src/client/julia/src/Sequence.jl @@ -1,8 +1,19 @@ -export seqChan, seqChan!, samplesPerStep, samplesPerStep!, stepsPerFrame!, AbstractSequence, SimpleSequence, sequence!, clearSequence!, HoldBorderRampingSequence, StartUpSequence, SequenceLUT, SimpleRampingSequence, seqTiming +export seqChan, + seqChan!, + samplesPerStep, + samplesPerStep!, + stepsPerFrame!, + AbstractSequence, + SimpleSequence, + sequence!, + clearSequence!, + HoldBorderRampingSequence, + StartUpSequence, + SequenceLUT, + SimpleRampingSequence, + seqTiming """ - seqChan(rp::RedPitaya) - Return the number of sequence channel. """ seqChan(rp::RedPitaya) = query(rp, scpiCommand(seqChan), scpiReturn(seqChan)) @@ -10,8 +21,6 @@ scpiCommand(::typeof(seqChan)) = "RP:DAC:SEQ:CHan?" scpiReturn(::typeof(seqChan)) = Int64 """ - seqChan!(rp::RedPitaya, value) - Set the number of sequence channel. Valid values are between `1` and `6`. Return `true` if the command was successful. """ function seqChan!(rp::RedPitaya, value) @@ -21,11 +30,9 @@ function seqChan!(rp::RedPitaya, value) return query(rp, scpiCommand(seqChan!, value), scpiReturn(seqChan!)) end scpiCommand(::typeof(seqChan!), value) = string("RP:DAC:SEQ:CHan ", Int64(value)) -scpiReturn(::typeof(seqChan!)) = Bool +scpiReturn(::typeof(seqChan!)) = Bool """ - samplesPerStep(rp::RedPitaya) - Return the number of samples per sequence step. """ samplesPerStep(rp::RedPitaya) = query(rp, scpiCommand(samplesPerStep), scpiReturn(samplesPerStep)) @@ -33,8 +40,6 @@ scpiCommand(::typeof(samplesPerStep)) = "RP:DAC:SEQ:SAMP?" scpiReturn(::typeof(samplesPerStep)) = Int64 """ - samplesPerStep!(rp::RedPitaya, value::Integer) - Set the number of samples per sequence step. Return `true` if the command was successful. """ function samplesPerStep!(rp::RedPitaya, value::Integer) @@ -44,8 +49,6 @@ scpiCommand(::typeof(samplesPerStep!), value) = string("RP:DAC:SEQ:SAMP ", value scpiReturn(::typeof(samplesPerStep!)) = Bool """ - stepsPerFrame!(rp::RedPitaya, stepsPerFrame) - Set the number of samples per steps s.t. `stepsPerFrame` sequence steps in a frame. See [`samplesPerPeriod!`](@ref), [`periodsPerFrame!`](@ref), [`samplesPerStep!`](@ref). @@ -57,8 +60,6 @@ function stepsPerFrame!(rp::RedPitaya, stepsPerFrame) end """ - setSequence!(rp::RedPitaya) - Instruct the server to set the current configured sequence for the next acquisition. """ setSequence!(rp::RedPitaya) = query(rp, scpiCommand(setSequence!), scpiReturn(setSequence!)) @@ -66,8 +67,6 @@ scpiCommand(::typeof(setSequence!)) = "RP:DAC:SEQ:SET" scpiReturn(::typeof(setSequence!)) = Bool """ - clearSequence!(rp::RedPitaya) - Instruct the server to remove all sequences from its list. Return `true` if the command was successful. """ clearSequence!(rp::RedPitaya) = query(rp, scpiCommand(clearSequence!), scpiReturn(clearSequence!)) @@ -90,15 +89,11 @@ repetitions(seq::SequenceLUT) = seq.repetitions length(seq::SequenceLUT) = seq.repetitions * size(seq.values, 2) """ - AbstractSequence - Abstract struct of client-side representation of a sequence. """ abstract type AbstractSequence end """ - SimpleSequence <: AbstractSequence - Struct representing a sequence in which the server directly takes the values from the given LUT. """ struct SimpleSequence <: AbstractSequence @@ -114,15 +109,31 @@ struct SimpleSequence <: AbstractSequence - `repetitions::Int32`: the number of times the sequence should be repeated - `emable::Union{Array{Bool}, Nothing}`: matrix containing enable flags """ - function SimpleSequence(lut::Array{Float32}, repetitions::Integer, enable::Union{Array{Bool}, Nothing}=nothing) + function SimpleSequence( + lut::Array{Float32}, + repetitions::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, + ) if !isnothing(enable) && size(lut) != size(enable) throw(DimensionMismatch("Size of enable LUT does not match size of value LUT")) end return new(SequenceLUT(lut, repetitions), enable) end end -SimpleSequence(lut::Array{T}, repetitions::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = SimpleSequence(map(Float32, lut), repetitions, enable) -SimpleSequence(lut::Vector{T}, repetitions::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = SimpleSequence(reshape(lut, 1, :), repetitions, enable) +function SimpleSequence( + lut::Array{T}, + repetitions::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return SimpleSequence(map(Float32, lut), repetitions, enable) +end +function SimpleSequence( + lut::Vector{T}, + repetitions::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return SimpleSequence(reshape(lut, 1, :), repetitions, enable) +end enableLUT(seq::SimpleSequence) = seq.enable valueLUT(seq::SimpleSequence) = seq.lut @@ -131,12 +142,17 @@ rampDownLUT(seq::SimpleSequence) = nothing abstract type RampingSequence <: AbstractSequence end -struct SimpleRampingSequence <: AbstractSequence +struct SimpleRampingSequence <: AbstractSequence lut::SequenceLUT enable::Union{Array{Bool}, Nothing} rampUp::SequenceLUT rampDown::SequenceLUT - function SimpleRampingSequencee(lut::SequenceLUT, up::SequenceLUT, down::SequenceLUT, enable::Union{Array{Bool}, Nothing}=nothing) + function SimpleRampingSequencee( + lut::SequenceLUT, + up::SequenceLUT, + down::SequenceLUT, + enable::Union{Array{Bool}, Nothing} = nothing, + ) if !isnothing(enable) && size(values(lut)) != size(enable) throw(DimensionMismatch("Size of enable LUT does not match size of value LUT")) end @@ -152,7 +168,7 @@ rampDownLUT(seq::SimpleRampingSequence) = nothing function timePerStep(rp::RedPitaya) dec = decimation(rp) perStep = samplesPerStep(rp) - return perStep/(125e6/dec) + return perStep / (125e6 / dec) end struct HoldBorderRampingSequence <: RampingSequence @@ -162,15 +178,18 @@ struct HoldBorderRampingSequence <: RampingSequence rampDown::SequenceLUT """ - HoldBorderRampingSequence(lut::Array{Float32}, repetitions::Integer, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) - Constructor for `HoldBorderRampingSequence`. # Arguments - `lut`,`repetitions`,`enable` are used the same as for a `SimpleSequence` - `rampingSteps` is the number of steps the first and last value of the given sequence are repeated before the sequence is started """ - function HoldBorderRampingSequence(lut::Array{Float32}, repetitions::Integer, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) + function HoldBorderRampingSequence( + lut::Array{Float32}, + repetitions::Integer, + rampingSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, + ) if !isnothing(enable) && size(lut) != size(enable) throw(DimensionMismatch("Size of enable LUT does not match size of value LUT")) end @@ -180,12 +199,26 @@ struct HoldBorderRampingSequence <: RampingSequence end end -HoldBorderRampingSequence(lut::Array{T}, repetitions::Integer, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = HoldBorderRampingSequence(map(Float32, lut), repetitions, rampingSteps, enable) -HoldBorderRampingSequence(lut::Vector{T}, repetitions::Integer, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = HoldBorderRampingSequence(reshape(lut, 1, :), repetitions, rampingSteps, enable) +function HoldBorderRampingSequence( + lut::Array{T}, + repetitions::Integer, + rampingSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return HoldBorderRampingSequence(map(Float32, lut), repetitions, rampingSteps, enable) +end +function HoldBorderRampingSequence( + lut::Vector{T}, + repetitions::Integer, + rampingSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return HoldBorderRampingSequence(reshape(lut, 1, :), repetitions, rampingSteps, enable) +end -function HoldBorderRampingSequence(rp::RedPitaya, lut, repetitions, enable=nothing) - rampTime = maximum([rampingDAC(rp,i) for i=1:2 if enableRamping(rp, i)]) - rampingSteps = Int64(ceil(rampTime/timePerStep(rp))) +function HoldBorderRampingSequence(rp::RedPitaya, lut, repetitions, enable = nothing) + rampTime = maximum([rampingDAC(rp, i) for i ∈ 1:2 if enableRamping(rp, i)]) + rampingSteps = Int64(ceil(rampTime / timePerStep(rp))) return HoldBorderRampingSequence(lut, repetitions, rampingSteps, enable) end @@ -198,18 +231,48 @@ struct ConstantRampingSequence <: RampingSequence lut::SequenceLUT enable::Union{Array{Bool}, Nothing} ramping::SequenceLUT - - function ConstantRampingSequence(lut::Array{Float32}, repetitions::Integer, rampingValue::Float32, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) + + function ConstantRampingSequence( + lut::Array{Float32}, + repetitions::Integer, + rampingValue::Float32, + rampingSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, + ) if !isnothing(enable) && size(lut) != size(enable) throw(DimensionMismatch("Size of enable LUT does not match size of value LUT")) end - rampingLut = SequenceLUT([rampingValue for i = 1:size(lut, 1)], rampingSteps) + rampingLut = SequenceLUT([rampingValue for i ∈ 1:size(lut, 1)], rampingSteps) return new(SequenceLUT(lut, repetitions), enable, rampingLut) end end -ConstantRampingSequence(lut::Array{T}, repetitions::Integer, rampingValue::Real, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = ConstantRampingSequence(map(Float32, lut), repetitions, Float32(rampingValue), rampingSteps, enable) -ConstantRampingSequence(lut::Vector{Float32}, repetitions::Integer, rampingValue::Float32, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) = ConstantRampingSequence(reshape(lut, 1, :), repetitions, rampingValue, rampingSteps, enable) -ConstantRampingSequence(lut::Vector{T}, repetitions::Integer, rampingValue::Real, rampingSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = ConstantRampingSequence(reshape(lut, 1, :), repetitions, rampingValue, rampingSteps, enable) +function ConstantRampingSequence( + lut::Array{T}, + repetitions::Integer, + rampingValue::Real, + rampingSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return ConstantRampingSequence(map(Float32, lut), repetitions, Float32(rampingValue), rampingSteps, enable) +end +function ConstantRampingSequence( + lut::Vector{Float32}, + repetitions::Integer, + rampingValue::Float32, + rampingSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) + return ConstantRampingSequence(reshape(lut, 1, :), repetitions, rampingValue, rampingSteps, enable) +end +function ConstantRampingSequence( + lut::Vector{T}, + repetitions::Integer, + rampingValue::Real, + rampingSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return ConstantRampingSequence(reshape(lut, 1, :), repetitions, rampingValue, rampingSteps, enable) +end enableLUT(seq::ConstantRampingSequence) = seq.enable valueLUT(seq::ConstantRampingSequence) = seq.lut @@ -221,7 +284,13 @@ struct StartUpSequence <: RampingSequence enable::Union{Array{Bool}, Nothing} rampUp::SequenceLUT rampDown::SequenceLUT - function StartUpSequence(lut::Array{Float32}, repetitions::Integer, rampingSteps::Integer, startUpSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) + function StartUpSequence( + lut::Array{Float32}, + repetitions::Integer, + rampingSteps::Integer, + startUpSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, + ) if !isnothing(enable) && size(lut) != size(enable) throw(DimensionMismatch("Size of enable LUT does not match size of value LUT")) end @@ -230,11 +299,11 @@ struct StartUpSequence <: RampingSequence end upLut = zeros(Float32, size(lut, 1), rampingSteps) if startUpSteps > 0 - for i = 0:startUpSteps-1 - upLut[:, end-i] = lut[:, end-(i%size(lut, 2))] + for i ∈ 0:(startUpSteps - 1) + upLut[:, end - i] = lut[:, end - (i % size(lut, 2))] end - for i = 1:rampingSteps - startUpSteps - upLut[:, i] = upLut[:, end-(startUpSteps-1)] + for i ∈ 1:(rampingSteps - startUpSteps) + upLut[:, i] = upLut[:, end - (startUpSteps - 1)] end end up = SequenceLUT(upLut, 1) @@ -242,9 +311,25 @@ struct StartUpSequence <: RampingSequence return new(SequenceLUT(lut, repetitions), enable, up, down) end end -StartUpSequence(lut::Array{T}, repetitions::Integer, rampingSteps::Integer, startUpSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = StartUpSequence(map(Float32, lut), repetitions, rampingSteps, startUpSteps, enable) +function StartUpSequence( + lut::Array{T}, + repetitions::Integer, + rampingSteps::Integer, + startUpSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return StartUpSequence(map(Float32, lut), repetitions, rampingSteps, startUpSteps, enable) +end #StartUpSequence(lut::Vector{Float32}, repetitions::Integer, rampingSteps::Integer, startUpSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) = StartUpSequence(reshape(lut, 1, :), repetitions, rampingSteps, startUpSteps, enable) -StartUpSequence(lut::Vector{T}, repetitions::Integer, rampingSteps::Integer, startUpSteps::Integer, enable::Union{Array{Bool}, Nothing}=nothing) where T <: Real = StartUpSequence(reshape(lut, 1, :), repetitions, rampingSteps, startUpSteps, enable) +function StartUpSequence( + lut::Vector{T}, + repetitions::Integer, + rampingSteps::Integer, + startUpSteps::Integer, + enable::Union{Array{Bool}, Nothing} = nothing, +) where {T <: Real} + return StartUpSequence(reshape(lut, 1, :), repetitions, rampingSteps, startUpSteps, enable) +end enableLUT(seq::StartUpSequence) = seq.enable valueLUT(seq::StartUpSequence) = seq.lut @@ -252,19 +337,19 @@ rampUpLUT(seq::StartUpSequence) = seq.rampUp rampDownLUT(seq::StartUpSequence) = seq.rampDown function computeRamping(dec, samplesPerStep, stepsPerSeq, rampTime, rampFraction) - bandwidth = 125e6/dec + bandwidth = 125e6 / dec samplesPerRotation = samplesPerStep * stepsPerSeq - totalRotations = Int32(ceil(rampTime/(samplesPerRotation/bandwidth))) + totalRotations = Int32(ceil(rampTime / (samplesPerRotation / bandwidth))) totalSteps = totalRotations * stepsPerSeq - steps = Int32(ceil(rampTime * rampFraction/(samplesPerStep/bandwidth))) + steps = Int32(ceil(rampTime * rampFraction / (samplesPerStep / bandwidth))) #@show samplesPerRotation totalRotations totalSteps steps rampTime rampFraction return (steps, totalSteps) end -computeRamping(rp::RedPitaya, stepsPerSeq ,rampTime, rampFraction) = computeRamping(decimation(rp), samplesPerStep(rp), stepsPerSeq, rampTime, rampFraction) +function computeRamping(rp::RedPitaya, stepsPerSeq, rampTime, rampFraction) + return computeRamping(decimation(rp), samplesPerStep(rp), stepsPerSeq, rampTime, rampFraction) +end """ - sequence!(rp::RedPitaya, seq::AbstractSequence) - Transmit the client-side representation `seq` to the server and append it to the current list of sequences. Return `true` if the required commands were successful. """ function sequence!(rp::RedPitaya, seq::AbstractSequence) @@ -324,14 +409,14 @@ end function seqTiming(seq::AbstractSequence) up = 0 - if !isnothing(rampUpLUT(seq)) + if !isnothing(rampUpLUT(seq)) up = 0 + length(rampUpLUT(seq)) end start = up down = length(valueLUT(seq)) + start finish = down - if !isnothing(rampUpLUT(seq)) + if !isnothing(rampUpLUT(seq)) finish = down + length(rampUpLUT(seq)) end - return (start=start, down=down, finish=finish) -end \ No newline at end of file + return (start = start, down = down, finish = finish) +end diff --git a/src/client/julia/src/SlowIO.jl b/src/client/julia/src/SlowIO.jl index f3c80011..ec3a7c96 100644 --- a/src/client/julia/src/SlowIO.jl +++ b/src/client/julia/src/SlowIO.jl @@ -1,25 +1,26 @@ export slowDAC! """ - slowDAC!(rp::RedPitaya, channel::Int64, val::Int64) - Set the value of the slow DAC channel `channel` to the value `val`. Return `true` if the command was successful. # Example + ```julia julia> slowDAC!(rp, 1, 500) true ``` """ slowDAC!(rp::RedPitaya, channel, val) = query(rp, scpiCommand(slowDAC!, channel, val), scpiReturn(slowDAC!)) -scpiCommand(::typeof(slowDAC!), channel::Int64, val::Int64) = string("RP:PDM:CHannel", channel, ":NextValueVolt ", val) +function scpiCommand(::typeof(slowDAC!), channel::Int64, val::Int64) + return string("RP:PDM:CHannel", channel, ":NextValueVolt ", val) +end scpiReturn(::typeof(slowDAC!)) = Bool export slowDACClockDivider! """ - slowDACClockDivider!(rp::RedPitaya, val::Int32) - Set the clock divider of the slow DAC. + # Example + ```julia julia> slowDACClockDivider!(rp, 8) @@ -27,16 +28,18 @@ julia>slowDACClockDivider(rp) 8 ``` """ -slowDACClockDivider!(rp::RedPitaya, val) = query(rp, scpiCommand(slowDACClockDivider!, val), scpiReturn(slowDACClockDivider!)) +function slowDACClockDivider!(rp::RedPitaya, val) + return query(rp, scpiCommand(slowDACClockDivider!, val), scpiReturn(slowDACClockDivider!)) +end scpiCommand(::typeof(slowDACClockDivider!), val::Int32) = string("RP:PDM:ClockDivider ", val) scpiReturn(::typeof(slowDACClockDivider!)) = Bool export slowDACClockDivider """ - slowDACClockDivider(rp::RedPitaya) - Get the clock divider of the slow DAC. + # Example + ```julia julia> slowDACClockDivider!(rp, 8) @@ -44,17 +47,18 @@ julia>slowDACClockDivider(rp) 8 ``` """ -slowDACClockDivider(rp::RedPitaya) = query(rp, scpiCommand(slowDACClockDivider), scpiReturn(slowDACClockDivider)) +function slowDACClockDivider(rp::RedPitaya) + return query(rp, scpiCommand(slowDACClockDivider), scpiReturn(slowDACClockDivider)) +end scpiCommand(::typeof(slowDACClockDivider)) = "RP:PDM:ClockDivider?" scpiReturn(::typeof(slowDACClockDivider)) = Int32 export slowDAC! """ -slowADC(rp::RedPitaya, channel::Int64) - Get the value of the XADC channel `channel`. # Example + ```julia julia> slowADC(rp, 1) 0.0 @@ -66,8 +70,6 @@ scpiReturn(::typeof(slowADC)) = Float64 export DIODirectionType, DIO_IN, DIO_OUT """ - DIODirectionType - Represent the different DIO directions. Valid value are `DIO_IN` and `DIO_OUT`. See [`DIODirection`](@ref), [`DIODirection!`](@ref). @@ -76,8 +78,6 @@ See [`DIODirection`](@ref), [`DIODirection!`](@ref). export DIOPins, DIO7_P, DIO7_N, DIO6_P, DIO6_N, DIO5_N, DIO4_N, DIO3_N, DIO2_N """ - DIOPins - Represent the different DIO pins. Valid value are `DIO7_P`, `DIO7_N`, `DIO6_P`, `DIO6_N`, `DIO5_N`, `DIO4_N`, `DIO3_N` and `DIO2_N`. @@ -98,8 +98,6 @@ DIOPinToCommand(pin::DIOPins) = replace(join(split(string(pin), "_"), ":Side"), export isValidDIOPin """ -isValidDIOPin(pin::String) - Check if a given string is an allowed value for the DIO pin names. See [`DIOPins`](@ref). @@ -108,10 +106,10 @@ isValidDIOPin(pin::String) = pin in string.(instances(DIOPins)) export DIODirection! """ - DIODirection!(rp::RedPitaya, pin::DIOPins, direction::DIODirectionType) - Set the direction of DIO pin `pin` to the value `direction`. + # Example + ```julia julia> DIODirection!(rp, DIO7_P, DIO_OUT) @@ -119,16 +117,20 @@ julia>DIODirection(rp, DIO7_P) DIO_OUT ``` """ -DIODirection!(rp::RedPitaya, pin, direction) = query(rp, scpiCommand(DIODirection!, pin, direction), scpiReturn(DIODirection!)) -scpiCommand(::typeof(DIODirection!), pin::DIOPins, direction::DIODirectionType) = string("RP:DIO:", DIOPinToCommand(pin), ":DIR ", (direction == DIO_IN ? "IN" : "OUT")) +function DIODirection!(rp::RedPitaya, pin, direction) + return query(rp, scpiCommand(DIODirection!, pin, direction), scpiReturn(DIODirection!)) +end +function scpiCommand(::typeof(DIODirection!), pin::DIOPins, direction::DIODirectionType) + return string("RP:DIO:", DIOPinToCommand(pin), ":DIR ", (direction == DIO_IN ? "IN" : "OUT")) +end scpiReturn(::typeof(DIODirection!)) = Bool export DIODirection """ - DIODirection(rp::RedPitaya, pin::DIOPins) - Get the direction of DIO pin `pin`. + # Example + ```julia julia> DIODirection!(rp, DIO7_P, DIO_OUT) @@ -136,18 +138,20 @@ julia>DIODirection(rp, DIO7_P) DIO_OUT ``` """ -DIODirection(rp::RedPitaya, pin) = parseReturn(DIODirection, query(rp, scpiCommand(DIODirection, pin), scpiReturn(DIODirection))) +function DIODirection(rp::RedPitaya, pin) + return parseReturn(DIODirection, query(rp, scpiCommand(DIODirection, pin), scpiReturn(DIODirection))) +end scpiCommand(::typeof(DIODirection), pin) = scpiCommand(DIODirection, stringToEnum(DIOPins, pin)) scpiCommand(::typeof(DIODirection), pin::DIOPins) = string("RP:DIO:", DIOPinToCommand(pin), ":DIR?") scpiReturn(::typeof(DIODirection)) = String -parseReturn(::typeof(DIODirection), ret) = stringToEnum(DIODirectionType, "DIO_"*ret) +parseReturn(::typeof(DIODirection), ret) = stringToEnum(DIODirectionType, "DIO_" * ret) export DIO! """ -DIO!(rp::RedPitaya, pin::DIOPins, val::Bool) - Set the value of DIO pin `pin` to the value `val`. + # Example + ```julia julia> DIO!(rp, DIO7_P, true) true @@ -159,17 +163,19 @@ function DIO!(rp::RedPitaya, pin, val) end return query(rp, scpiCommand(DIO!, pin, val), scpiReturn(DIO!)) end -scpiCommand(::typeof(DIO!), pin::DIOPins, val::Bool) = string("RP:DIO:", DIOPinToCommand(pin), " ", (val ? "ON" : "OFF")) +function scpiCommand(::typeof(DIO!), pin::DIOPins, val::Bool) + return string("RP:DIO:", DIOPinToCommand(pin), " ", (val ? "ON" : "OFF")) +end scpiReturn(::typeof(DIO!)) = Bool export DIO """ - DIO(rp::RedPitaya, pin::DIOPins) - Get the value of DIO pin `pin`. + # Example + ```julia -julia>DIO(rp, DIO7_P) +julia > DIO(rp, DIO7_P) true ``` """ diff --git a/src/client/julia/src/Utility.jl b/src/client/julia/src/Utility.jl index 98692827..3b00d9ee 100644 --- a/src/client/julia/src/Utility.jl +++ b/src/client/julia/src/Utility.jl @@ -1,40 +1,44 @@ # https://discourse.julialang.org/t/how-to-extract-a-file-in-a-zip-archive-without-using-os-specific-tools/34585/5 -function unzip(file; exdir="") - fileFullPath = isabspath(file) ? file : joinpath(pwd(),file) +function unzip(file; exdir = "") + fileFullPath = isabspath(file) ? file : joinpath(pwd(), file) basePath = dirname(fileFullPath) - outPath = (exdir == "" ? basePath : (isabspath(exdir) ? exdir : joinpath(pwd(),exdir))) + outPath = (exdir == "" ? basePath : (isabspath(exdir) ? exdir : joinpath(pwd(), exdir))) isdir(outPath) ? "" : mkdir(outPath) zarchive = ZipFile.Reader(fileFullPath) - for f in zarchive.files - fullFilePath = joinpath(outPath,f.name) - if (endswith(f.name,"/") || endswith(f.name,"\\")) - mkdir(fullFilePath) - else - write(fullFilePath, read(f)) - end + for f ∈ zarchive.files + fullFilePath = joinpath(outPath, f.name) + if (endswith(f.name, "/") || endswith(f.name, "\\")) + mkdir(fullFilePath) + else + write(fullFilePath, read(f)) + end end close(zarchive) return outPath end -function downloadImage(url::URI; force=false) +function downloadImage(url::URI; force = false) p = Progress(1000, 0.5, "Downloading image...") function calcDownloadProgress(total::Integer, now::Integer) - fraction = now/total*1000 + fraction = now / total * 1000 fraction = isnan(fraction) ? 1.0 : fraction fraction = round(Int64, fraction) return fraction end splittedUrl = URIs.splitpath(url) fileName = splittedUrl[end] - tagName = splittedUrl[end-1] + tagName = splittedUrl[end - 1] scratch = @get_scratch!("releases") releaseFolder = mkpath(joinpath(scratch, tagName)) output = joinpath(releaseFolder, fileName) if !isfile(output) || force - Downloads.download(string(url), output, progress=(total, now) -> ProgressMeter.update!(p, calcDownloadProgress(total, now))) + Downloads.download( + string(url), + output; + progress = (total, now) -> ProgressMeter.update!(p, calcDownloadProgress(total, now)), + ) else @debug "The image at `$url` does already exist and was thus not downloaded. Use `force=true` to download it anyways." end @@ -45,7 +49,7 @@ downloadImage(tagName::String; kwargs...) = downloadImage(getImageURL(tagName); function getImageURL(tagName::String) rels = releases("tknopp/RedPitayaDAQServer")[1] - relIdx = findfirst([rel.tag_name for rel in rels] .== tagName) + relIdx = findfirst([rel.tag_name for rel ∈ rels] .== tagName) if !isnothing(relIdx) rel = rels[relIdx] assets = rel.assets @@ -74,7 +78,7 @@ Return a vector of release tags """ function listReleaseTags() rels = releases("tknopp/RedPitayaDAQServer")[1] - return [rel.tag_name for rel in rels] + return [rel.tag_name for rel ∈ rels] end export latestReleaseTag @@ -86,29 +90,28 @@ Return the latest release tag. See also [`listReleaseTags`](@ref), [`update!`](@ref). # Examples + ```julia julia> update!("192.168.1.100", latestReleaseTag()) ... ``` """ -function latestReleaseTag() - return listReleaseTags()[1] -end +latestReleaseTag() = listReleaseTags()[1] -function downloadAndExtractImage(tagName::String; force=false) - imageZipPath = downloadImage(tagName, force=force) +function downloadAndExtractImage(tagName::String; force = false) + imageZipPath = downloadImage(tagName; force = force) imagePath = joinpath(dirname(imageZipPath), "extracted") if isdir(imagePath) if force - rm(imagePath, recursive=true, force=true) - return unzip(imageZipPath, exdir=imagePath) + rm(imagePath; recursive = true, force = true) + return unzip(imageZipPath; exdir = imagePath) else @debug "The image with tag `$tagName` was already extracted and is thus not being extracted again. Use `force=true` to extract it anyways." return imagePath end else - return unzip(imageZipPath, exdir=imagePath) + return unzip(imageZipPath; exdir = imagePath) end end @@ -119,20 +122,20 @@ function uploadBitfile(ip::String, bitfilePath::String) imagePath = extractedImagePath(tagName) keyPath = joinpath(imagePath, "apps", "RedPitayaDAQServer", "rootkey") argument = Cmd(["-i", keyPath, bitfile, "root@$(ip):/media/mmcblk0p1/apps/RedPitayaDAQServer/bitfiles"]) - run(`$(scp()) $argument`) + return run(`$(scp()) $argument`) end function uploadBitfiles(ip::String, tagName::String) imagePath = extractedImagePath(tagName) bitfilePath = joinpath(imagePath, "apps", "RedPitayaDAQServer", "bitfiles") - bitfiles = [joinpath(bitfilePath, bitfile) for bitfile in readdir(bitfilePath)] - - for bitfile in bitfiles + bitfiles = [joinpath(bitfilePath, bitfile) for bitfile ∈ readdir(bitfilePath)] + + for bitfile ∈ bitfiles uploadBitfile(ip, bitfile) end end -function checkDependencies() +checkDependencies() = if Sys.iswindows() success(`where ssh`) || error("'ssh' not found.") success(`where scp`) || error("'scp' not found.") @@ -142,7 +145,6 @@ function checkDependencies() success(`which scp`) || error("'scp' not found.") success(`which git`) || error("'git' not found.") end -end function prepareProject(tagName::String) @info "Downloading tagged release" @@ -153,11 +155,11 @@ function prepareProject(tagName::String) # Prepare folder for RPs without internet connection @info "Preparing RedPitayaDAQServer folder" argument = Cmd(["config", "--add", "safe.directory", projectPath]) - run(setenv(`git $argument`, dir=projectPath)) + run(setenv(`git $argument`; dir = projectPath)) argument = Cmd(["config", "--add", "safe.directory", joinpath(projectPath, "libs", "scpi-parser")]) - run(setenv(`git $argument`, dir=projectPath)) + run(setenv(`git $argument`; dir = projectPath)) argument = Cmd(["submodule", "update", "--init", "--force", "--remote"]) - run(setenv(`git $argument`, dir=projectPath)) + run(setenv(`git $argument`; dir = projectPath)) chmod(keyPath, 0o400) # Otherwise private key is not accepted by ssh as it is unsecure return projectPath, keyPath end @@ -188,8 +190,8 @@ function updateRedPitaya!(ip::String, projectPath, keyPath) # Wait for reboot sleep(2) @info "Attempting to connect to RedPitaya $ip" - for i=1:5 - try + for i ∈ 1:5 + try rp = RedPitaya(ip) @info "Connected to RedPitaya $ip" @info "Successfully updated RedPitaya $ip" @@ -197,7 +199,7 @@ function updateRedPitaya!(ip::String, projectPath, keyPath) catch ex if i == 5 @warn "Could not connect to RedPitaya $ip in $i attempts. Try again manually" - else + else @info "Failed to connect. Retry in 10 seconds" sleep(10) end @@ -212,13 +214,13 @@ Update the Red Pitaya with the release from the given tag. function update!(ip::String, tagName::String) checkDependencies() projectPath, keyPath = prepareProject(tagName) - updateRedPitaya!(ip, projectPath, keyPath) + return updateRedPitaya!(ip, projectPath, keyPath) end update!(rp::RedPitaya, tagName::String) = update!(rp.host, tagName::String) -function update!(rpc::RedPitayaCluster, tagName::String) +function update!(rpc::RedPitayaCluster, tagName::String) checkDependencies() projectPath, keyPath = prepareProject(tagName) - @sync for rp in rpc + @sync for rp ∈ rpc @async updateRedPitaya!(rp.host, projectPath, keyPath) end end diff --git a/src/client/julia/test/runtests.jl b/src/client/julia/test/runtests.jl index 1ada1dc8..471c7ca2 100644 --- a/src/client/julia/test/runtests.jl +++ b/src/client/julia/test/runtests.jl @@ -1,9 +1,49 @@ using RedPitayaDAQServer using Test using Aqua +using JuliaFormatter +using Sockets + +isCIRun = + haskey(ENV, "GITHUB_ACTIONS") || + haskey(ENV, "TRAVIS") || + haskey(ENV, "CIRCLECI") || + haskey(ENV, "GITLAB_CI") @testset "RedPitayaDAQServer" begin + @testset "Formatting" begin + @error pathof(RedPitayaDAQServer) + formatted = format(pathof(RedPitayaDAQServer); verbose = false) + + if !formatted && !isCIRun + @info "Please re-run tests since now everything should be formatted." + end + @test formatted == true + end + @testset "Aqua" begin Aqua.test_all(RedPitayaDAQServer) end -end \ No newline at end of file + + @testset "SingleRP" begin + if !isfile("config.jl") + @info "No config given for hardware-based tests. The tests will be skipped. If you want to run hardware-based tests, please create a file named `config.jl` in the `test` folder. The file should contain a variable named `singleIP` with a string giving the IP of the device under test." + else + include("config.jl") + + if @isdefined singleIP + port = 5025 + try + sock = Sockets.connect(singleIP, port) + close(sock) + + include("single.jl") + catch e + @warn "No connection can be established to RP at `$singleIP:$port` for hardware-based tests. The tests will be skipped." + end + else + @info "No IP for a single RP was given by specifying `singleIP` for hardware-based tests. The tests will be skipped." + end + end + end +end diff --git a/src/client/julia/test/single.jl b/src/client/julia/test/single.jl new file mode 100644 index 00000000..ba91ce4d --- /dev/null +++ b/src/client/julia/test/single.jl @@ -0,0 +1,69 @@ +rp = RedPitaya(singleIP) + +@testset "Acquisition.jl" begin end + +@testset "ADC.jl" begin + @test decimation!(rp, 8) + @test decimation(rp) == 8 + @test decimation!(rp, 16) + @test decimation(rp) == 16 + + @test numChan(rp) == 2 # A single RP always has two ADC Channels + + samplesPerPeriod!(rp, 42) + @test samplesPerPeriod(rp) == 42 + samplesPerPeriod!(rp, 625) + @test samplesPerPeriod(rp) == 625 + + @test periodsPerFrame!(rp, 42) + @test samplesPerPeriod(rp) == 42 + @test periodsPerFrame!(rp, 1) + @test periodsPerFrame(rp) == 1 + + @test currentFrame(rp) == 0 # The aquisition is not running + @test currentPeriod(rp) == 0 # The aquisition is not running + @test currentWP(rp) == 0 # The aquisition is not running + + @test bufferSize(rp) == 2^24 + + @test masterTrigger!(rp, true) + @test masterTrigger(rp) == true + #TODO: Test changes in wp + @test masterTrigger!(rp, false) + + @test keepAliveReset!(rp, true) + @test keepAliveReset(rp) == true + @test keepAliveReset!(rp, false) + @test keepAliveReset(rp) == false + + @test triggerMode!(rp, EXTERNAL) + @test triggerMode(rp) == EXTERNAL + @test triggerMode!(rp, INTERNAL) + @test triggerMode(rp) == INTERNAL + + # We can only really test the trigger propagation in a cluster + @test triggerPropagation!(rp, true) + @test triggerPropagation(rp) == true + @test triggerPropagation!(rp, false) + @test triggerPropagation(rp) == false + + @warn " How do we test this?" + @test overwritten(rp) == "" + @test corrupted(rp) == "" + @test serverStatus(rp) == "" + @test readServerStatus(rp) = "" + + @warn " How do we test this?" + # performanceData(rp, numSamples = 0) + # readPerformanceData(rp, numSamples = 0) + # readADCPerformanceData(rp, numSamples = 0) + # readChunkMetaData(rp, reqWP, numSamples) + # readSamples!(rp, data::AbstractArray{Int16}) + + @warn " How do we test this?" + # readSamplesChunk_(rp::RedPitaya, reqWP::Int64, numSamples::Int64) + # readSamplesChunk_(rp::RedPitaya, reqWP::Int64, buffer::AbstractArray{Int16}) + # startPipelinedData(rp::RedPitaya, reqWP::Int64, numSamples::Int64, chunkSize::Int64) + @test stopTransmission(rp) == true +end +@error("TODO: Cont' here") \ No newline at end of file