-
Notifications
You must be signed in to change notification settings - Fork 318
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- How to use C# bindings to control libiio devices. - Configure Pluto and use RX and TX interfaces to send and receive data. - Nice to have for EZ questions such as: https://ez.analog.com/adieducation/university-program/f/q-a/574101/adalam-pluto-sdr-via-c Signed-off-by: Adrian Stanea <[email protected]>
- Loading branch information
1 parent
cfb6249
commit 9cd7307
Showing
1 changed file
with
158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* Copyright (C) 2023 Analog Devices, Inc. | ||
* Author: Adrian Stanea <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public License | ||
* as published by the Free Software Foundation; either version 2 | ||
* of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
* */ | ||
|
||
using System; | ||
using System.Globalization; | ||
using System.Reflection; | ||
using iio; | ||
|
||
[assembly: AssemblyTitle("Pluto_Example")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: System.Runtime.InteropServices.ComVisible(false)] | ||
[assembly: CLSCompliant(true)] | ||
namespace IIOCSharp | ||
{ | ||
static class PlutoExample | ||
{ | ||
const int INT16_BYTE_STEP = 2; | ||
const string URI = "ip:192.168.2.1"; | ||
const string TXLO = "1000000000"; | ||
const string TXBW = "5000000"; | ||
const string TXFS = "3000000"; | ||
const string RXLO = TXLO; | ||
const string RXBW = TXBW; | ||
const string RXFS = TXFS; | ||
const string GAIN_CTRL_MODE = "slow_attack"; | ||
const string HW_GAIN = "-30"; | ||
|
||
const string CHN_VOLTAGE0 = "voltage0"; | ||
const string CHN_VOLTAGE1 = "voltage1"; | ||
|
||
static void Main(string[] args) | ||
{ | ||
Context ctx = new Context(URI); | ||
if (ctx == null) | ||
{ | ||
Console.WriteLine("Unable to create IIO context"); | ||
return; | ||
} | ||
// get reference to devices | ||
var ctrl = ctx.find_device("ad9361-phy"); | ||
var tx_dac = ctx.find_device("cf-ad9361-dds-core-lpc"); | ||
var rx_adc = ctx.find_device("cf-ad9361-lpc"); | ||
|
||
rx_config(ctrl, RXLO, RXBW, RXFS, GAIN_CTRL_MODE); | ||
tx_config(ctrl, TXLO, TXBW, TXFS, HW_GAIN); | ||
|
||
// Enable all IQ channels | ||
enable_tx(tx_dac); | ||
enable_rx(rx_adc); | ||
|
||
// Cyclic buffer to output perdiodic waveforms | ||
uint samples_per_channel = Convert.ToUInt16(Math.Pow(2, 15)); | ||
|
||
var tx_buf = new IOBuffer(tx_dac, samples_per_channel, true); | ||
var rx_buff = new IOBuffer(rx_adc, samples_per_channel, false); | ||
|
||
byte[] iqBytes = generate_sine(Convert.ToInt32(samples_per_channel), RXFS); | ||
|
||
// Send data to TX buffer | ||
tx_buf.fill(iqBytes); | ||
tx_buf.push(); | ||
|
||
// read data on rx buffer | ||
const int num_readings = 10; | ||
(List<int> reals, List<int> imags) = read_rx_data(rx_buff, num_readings, Convert.ToInt32(samples_per_channel)); | ||
} | ||
|
||
static void rx_config(Device device, string rx_lo, string rx_bw, string rx_fs, string gain_ctrl_mode) | ||
{ | ||
var rx_chn = device.find_channel(CHN_VOLTAGE0, false); | ||
|
||
device.find_channel("RX_LO", true).find_attribute("frequency").write(rx_lo); | ||
rx_chn.find_attribute("rf_bandwidth").write(rx_bw); | ||
rx_chn.find_attribute("sampling_frequency").write(rx_fs); | ||
rx_chn.find_attribute("gain_control_mode").write(gain_ctrl_mode); | ||
} | ||
|
||
static void tx_config(Device device, string tx_lo, string tx_bw, string tx_fs, string hw_gain) | ||
{ | ||
var tx_chn = device.find_channel(CHN_VOLTAGE0, true); | ||
|
||
device.find_channel("TX_LO", true).find_attribute("frequency").write(tx_lo); | ||
tx_chn.find_attribute("rf_bandwidth").write(tx_bw); | ||
tx_chn.find_attribute("sampling_frequency").write(tx_fs); | ||
tx_chn.find_attribute("hardwaregain").write(hw_gain); | ||
} | ||
|
||
static void enable_tx(Device tx_adc) | ||
{ | ||
tx_adc.find_channel(CHN_VOLTAGE0, true).enable(); | ||
tx_adc.find_channel(CHN_VOLTAGE1, true).enable(); | ||
} | ||
|
||
static void enable_rx(Device rx_dac) | ||
{ | ||
rx_dac.find_channel(CHN_VOLTAGE0, false).enable(); | ||
rx_dac.find_channel(CHN_VOLTAGE1, false).enable(); | ||
} | ||
|
||
static byte[] generate_sine(int samples_per_channel, string rx_fs) | ||
{ | ||
const int fc = 10000; | ||
double ts = 1.0 / int.Parse(rx_fs, CultureInfo.CurrentCulture); | ||
double[] t = Enumerable.Range(0, samples_per_channel).Select(i => i * ts).ToArray(); | ||
|
||
double[] i = t.Select(x => (Math.Sin(2 * Math.PI * x * fc) * Math.Pow(2, 14))).ToArray(); | ||
double[] q = t.Select(x => (Math.Cos(2 * Math.PI * x * fc) * Math.Pow(2, 14))).ToArray(); | ||
|
||
Int16[] iq = new Int16[i.Length + q.Length]; | ||
for (int j = 0; j < i.Length; j++) | ||
{ | ||
iq[j * INT16_BYTE_STEP] = Convert.ToInt16(i[j]); | ||
iq[j * INT16_BYTE_STEP + 1] = Convert.ToInt16(q[j]); | ||
} | ||
|
||
byte[] iqBytes = iq.SelectMany(BitConverter.GetBytes).ToArray(); | ||
return iqBytes; | ||
} | ||
|
||
static (List<int> reals, List<int> imags) read_rx_data(IOBuffer rx_buff, int num_readings, int samples_per_channel) | ||
{ | ||
List<int> reals = new List<int>(); | ||
List<int> imags = new List<int>(); | ||
|
||
byte[] data = new byte[samples_per_channel]; | ||
for (int k = 0; k < num_readings; k++) | ||
{ | ||
rx_buff.refill(); | ||
rx_buff.read(data); | ||
|
||
Int16[] data_casted = new Int16[data.Length / INT16_BYTE_STEP]; | ||
Buffer.BlockCopy(data, 0, data_casted, 0, data.Length); | ||
for (int idx = 0; idx < data_casted.Length; idx += INT16_BYTE_STEP) | ||
{ | ||
reals.Add(data_casted[idx]); | ||
imags.Add(data_casted[idx + 1]); | ||
} | ||
} | ||
return (reals, imags); | ||
} | ||
} | ||
} |