0% found this document useful (0 votes)
0 views26 pages

OBW Code

This document provides a comprehensive guide on how to programmatically control the Agilent E4446A Spectrum Analyzer using C#. It includes a detailed C# code snippet for connecting to the analyzer, configuring settings, acquiring trace data, calculating the Occupied Bandwidth (OBW), and visualizing the results using Windows Forms. Additionally, it outlines steps for exporting the resulting chart as a PNG image.

Uploaded by

dhouha
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
0 views26 pages

OBW Code

This document provides a comprehensive guide on how to programmatically control the Agilent E4446A Spectrum Analyzer using C#. It includes a detailed C# code snippet for connecting to the analyzer, configuring settings, acquiring trace data, calculating the Occupied Bandwidth (OBW), and visualizing the results using Windows Forms. Additionally, it outlines steps for exporting the resulting chart as a PNG image.

Uploaded by

dhouha
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 26

Sure!

To control your Agilent E4446A Spectrum Analyzer programmatically via C#, you’ll
typically communicate using SCPI commands over a VISA interface (GPIB, LAN, or
USB). You can use NI VISA, Keysight IO Libraries, or Tek VISA to handle the
communication.

Here's a C# code snippet that:

 Connects to the spectrum analyzer


 Sets RBW, VBW, SPAN
 Acquires trace data
 Calculates the Occupied Bandwidth (99%)
 Identifies the lower and upper frequency bounds
 Places markers at those points
 Calculates the OBW as the frequency difference between them

You'll need:

 Keysight IO Libraries Suite installed


 VisaNS or Keysight VISA.NET reference in your C# project

C# Code Snippet (Occupied Bandwidth via SCPI)

using System;

using Ivi.Visa.Interop; // Add reference to VISA COM library

using System.Linq;

class OccupiedBandwidthCalc

static void Main()

ResourceManager rm = new ResourceManager();

FormattedIO488 io = new FormattedIO488();

try

// Connect to your instrument

io.IO = (IMessage)rm.Open("TCPIP0::192.168.0.10::inst0::INSTR"); // Change to your


IP/resource
// Clear previous states

io.WriteString("*CLS");

io.WriteString(":INIT:CONT OFF"); // Single sweep mode

// Setup frequency span, RBW, VBW

io.WriteString(":FREQ:CENT 1GHz"); // Center frequency

io.WriteString(":FREQ:SPAN 10MHz"); // Frequency span

io.WriteString(":BAND 100kHz"); // RBW

io.WriteString(":BAND:VID 100kHz"); // VBW

// Trigger a new sweep

io.WriteString(":INIT; *WAI");

// Get trace data

io.WriteString(":TRAC? TRACE1");

string traceData = io.ReadString();

// Parse trace data

double[] powerValues = traceData .Split(new[] { ',', '\n', '\r' },


StringSplitOptions.RemoveEmptyEntries) .Select(s => double.Parse(s)) .ToArray();

// Get frequency settings

io.WriteString(":FREQ:STAR?");

double startFreq = double.Parse(io.ReadString());

io.WriteString(":FREQ:STOP?");

double stopFreq = double.Parse(io.ReadString());


double freqStep = (stopFreq - startFreq) / (powerValues.Length - 1);

double[] freqArray = new double[powerValues.Length];

for (int i = 0; i < powerValues.Length; i++)

freqArray[i] = startFreq + i * freqStep;

// Calculate Occupied Bandwidth (99% power)

double totalPower = powerValues.Sum(p => Math.Pow(10, p / 10));

double targetPower = totalPower * 0.99;

int lowerIdx = 0, upperIdx = powerValues.Length - 1;

double currentSum = 0;

for (int i = 0; i < powerValues.Length; i++)

for (int j = i; j < powerValues.Length; j++)

currentSum = 0;

for (int k = i; k <= j; k++)

currentSum += Math.Pow(10, powerValues[k] / 10);

if (currentSum >= targetPower)

lowerIdx = i;

upperIdx = j;

goto Done;
}

Done:

double lowerFreq = freqArray[lowerIdx];

double upperFreq = freqArray[upperIdx];

double obw = upperFreq - lowerFreq;

// Place markers

io.WriteString($":CALC:MARK1:X {lowerFreq}");

io.WriteString($":CALC:MARK2:X {upperFreq}");

// Output result

Console.WriteLine($"Occupied Bandwidth (99%) = {obw / 1e6} MHz");

catch (Exception ex)

Console.WriteLine("Error: " + ex.Message);

Great question! To visualize the trace and highlight the Occupied Bandwidth (OBW)
region between the lower and upper frequency limits, you can use Windows Forms with a
charting library (like System.Windows.Forms.DataVisualization.Charting).

Let’s walk through how to plot the spectrum trace and highlight the OBW region on a
graph with markers at the boundaries
Step-by-Step Plan

🔧 1. Create a Windows Forms App (C#)

You can do this in Visual Studio:

 File > New > Project > "Windows Forms App (.NET Framework)"
 Add a Chart control to your form from the toolbox.

2. Reference Required Libraries

Make sure to import:

using System.Windows.Forms.DataVisualization.Charting;

Code Example to Plot Spectrum + OBW Region

Let’s assume you already have the following arrays from your analyzer:

double[] freqArray; // x-axis in Hz

double[] powerValues; // y-axis in dBm

double lowerFreq; // OBW start

double upperFreq; // OBW end

Now here's how you would display it:

private void PlotSpectrum(double[] freqArray, double[] powerValues, double lowerFreq,


double upperFreq)

chart1.Series.Clear();

chart1.ChartAreas.Clear();

chart1.ChartAreas.Add("MainArea");

var spectrumSeries = new Series("Spectrum");

spectrumSeries.ChartType = SeriesChartType.Line;

for (int i = 0; i < freqArray.Length; i++)


{

double freqMHz = freqArray[i] / 1e6; // Convert to MHz for display

spectrumSeries.Points.AddXY(freqMHz, powerValues[i]);

chart1.Series.Add(spectrumSeries);

// Add vertical strip to show OBW region

var obwStrip = new StripLine

IntervalOffset = lowerFreq / 1e6,

StripWidth = (upperFreq - lowerFreq) / 1e6,

BackColor = Color.FromArgb(50, Color.Yellow),

Text = "99% OBW",

TextAlignment = StringAlignment.Near,

Font = new Font("Arial", 8),

BorderColor = Color.Red,

BorderWidth = 1

};

chart1.ChartAreas[0].AxisX.StripLines.Add(obwStrip);

// Add markers

var lowerMarker = new Series("Lower OBW");

lowerMarker.ChartType = SeriesChartType.Point;
lowerMarker.MarkerStyle = MarkerStyle.Cross;

lowerMarker.MarkerSize = 10;

lowerMarker.Color = Color.Red;

var upperMarker = new Series("Upper OBW");

upperMarker.ChartType = SeriesChartType.Point;

upperMarker.MarkerStyle = MarkerStyle.Cross;

upperMarker.MarkerSize = 10;

upperMarker.Color = Color.Red;

int lowerIndex = Array.FindIndex(freqArray, f => f >= lowerFreq);

int upperIndex = Array.FindIndex(freqArray, f => f >= upperFreq);

lowerMarker.Points.AddXY(lowerFreq / 1e6, powerValues[lowerIndex]);

upperMarker.Points.AddXY(upperFreq / 1e6, powerValues[upperIndex]);

chart1.Series.Add(lowerMarker);

chart1.Series.Add(upperMarker);

// Label axes

chart1.ChartAreas[0].AxisX.Title = "Frequency (MHz)";

chart1.ChartAreas[0].AxisY.Title = "Power (dBm)";

Output Preview

This will result in:


 A line chart of the spectrum
 A shaded OBW region
 Two red cross markers showing where the 99% power bandwidth starts and ends

📝 Final Touch: Calling the Plot Function

Once you've calculated everything from the analyzer:

PlotSpectrum(freqArray, powerValues, lowerFreq, upperFreq);

Perfect — exporting the chart as a PNG image and saving it on your PC is super simple in a Windows
Forms app using the built-in Chart.SaveImage() method.

✅ Export Chart as PNG

Here’s a small function you can call after plotting the spectrum

private void SaveChartAsImage(string filePath)

try

chart1.SaveImage(filePath, ChartImageFormat.Png);

MessageBox.Show("Chart saved successfully to:\n" + filePath, "Export Complete",


MessageBoxButtons.OK, MessageBoxIcon.Information);

catch (Exception ex)

MessageBox.Show("Failed to save chart: " + ex.Message, "Error",


MessageBoxButtons.OK, MessageBoxIcon.Error);

📁 Example Usage

Let’s say you want to save the image to your desktop


string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

string fileName = "OBW_Spectrum.png";

string fullPath = Path.Combine(desktopPath, fileName);

SaveChartAsImage(fullPath);

You can also tie this to a button:

private void btnSaveChart_Click(object sender, EventArgs e)


{
string desktopPath =
Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string fullPath = Path.Combine(desktopPath, "OBW_Spectrum.png");

SaveChartAsImage(fullPath);
}

🧠 Optional: Use SaveFileDialog to Pick Location

If you want the user to choose where to save

SaveFileDialog sfd = new SaveFileDialog


{
Filter = "PNG Image|*.png",
Title = "Save Chart as Image",
FileName = "OBW_Spectrum.png"
};

if (sfd.ShowDialog() == DialogResult.OK)
{
SaveChartAsImage(sfd.FileName);
}

Great — you're close now! Let me walk you through exactly how to get these four things
from your Agilent E4446A spectrum analyzer using SCPI over C#.

These are the four things we want:

1. freqArray[]: the X-axis — frequencies corresponding to each power point


2. powerArray[]: the Y-axis — amplitude values from the analyzer (dBm)
3. lowerFreq: start of 99% occupied bandwidth
4. upperFreq: end of 99% occupied bandwidth

✅ 1. Set up & acquire trace data from the analyzer

✅ C# Code Snippet to get power values and frequency range


using Ivi.Visa.Interop;
using System.Linq;
public class AnalyzerDataAcquisition
{
public static (double[] freqArray, double[] powerArray, double
lowerFreq, double upperFreq) GetAnalyzerData()
{
ResourceManager rm = new ResourceManager();
FormattedIO488 io = new FormattedIO488();
io.IO = (IMessage)rm.Open("TCPIP0::192.168.0.10::inst0::INSTR"); //
Replace with your VISA address

io.WriteString("*CLS");
io.WriteString(":INIT:CONT OFF"); // Single sweep mode

// Configuration
io.WriteString(":FREQ:CENT 1GHz");
io.WriteString(":FREQ:SPAN 10MHz");
io.WriteString(":BAND 100kHz"); // RBW
io.WriteString(":BAND:VID 100kHz"); // VBW

// Trigger sweep
io.WriteString(":INIT; *WAI");

// Get power trace


io.WriteString(":TRAC? TRACE1");
string traceString = io.ReadString();

double[] powerArray = traceString


.Split(new[] { ',', '\n', '\r' },
StringSplitOptions.RemoveEmptyEntries)
.Select(s => double.Parse(s))
.ToArray();

// Get frequency range


io.WriteString(":FREQ:STAR?");
double startFreq = double.Parse(io.ReadString());

io.WriteString(":FREQ:STOP?");
double stopFreq = double.Parse(io.ReadString());

// Build freqArray
int pointCount = powerArray.Length;
double step = (stopFreq - startFreq) / (pointCount - 1);

double[] freqArray = new double[pointCount];


for (int i = 0; i < pointCount; i++)
freqArray[i] = startFreq + i * step;

// Calculate OBW (Occupied Bandwidth - 99% Power)


double totalPowerLinear = powerArray.Sum(p => Math.Pow(10, p /
10));
double targetPower = totalPowerLinear * 0.99;

int lowerIndex = 0, upperIndex = pointCount - 1;


double currentSum = 0;

for (int i = 0; i < pointCount; i++)


{
for (int j = i; j < pointCount; j++)
{
currentSum = 0;
for (int k = i; k <= j; k++)
currentSum += Math.Pow(10, powerArray[k] / 10);

if (currentSum >= targetPower)


{
lowerIndex = i;
upperIndex = j;
goto Done;
}
}
}

Done:
double lowerFreq = freqArray[lowerIndex];
double upperFreq = freqArray[upperIndex];

return (freqArray, powerArray, lowerFreq, upperFreq);


}
}

How to Use This in Your Code

After you call the function:

var data = AnalyzerDataAcquisition.GetAnalyzerData();

double[] freqArray = data.freqArray;


double[] powerArray = data.powerArray;
double lowerFreq = data.lowerFreq;
double upperFreq = data.upperFreq;

You now have:

 freqArray: all the frequencies (Hz)


 powerArray: corresponding power values (dBm)
 lowerFreq / upperFreq: the OBW limits (Hz)

📈 Next Step: Pass to Graph Function

Once you’ve got them, pass them to your chart plotting code

PlotSpectrum(freqArray, powerArray, lowerFreq, upperFreq);

Awesome! Here’s your complete working C# code, all combined into a single Windows
Forms App that:

1. Connects to your Agilent E4446A Spectrum Analyzer


2. Configures frequency, RBW, VBW, span
3. Gets the spectrum trace (freq + power arrays)
4. Calculates 99% Occupied Bandwidth (OBW)
5. Displays it in a chart
6. Adds OBW boundaries + markers
7. Allows you to export the graph to PNG

✅ Prerequisites

1. Create a Windows Forms App (.NET Framework) in Visual Studio


2. From Toolbox:
o Drag a Chart (System.Windows.Forms.DataVisualization.Charting) onto
your form
o Drag a Button to trigger the process (name: btnAcquire)
o Drag another Button to save the chart (name: btnSave)
3. Add a reference to Ivi.Visa.Interop (COM reference to use VISA

✅ Full C# Code

Paste this into Form1.cs:

using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using Ivi.Visa.Interop;

namespace OBW_Analyzer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

double[] freqArray, powerArray;


double lowerFreq, upperFreq;

private void btnAcquire_Click(object sender, EventArgs e)


{
try
{
// Connect and acquire data
AcquireAnalyzerData();

// Plot the spectrum


PlotSpectrum(freqArray, powerArray, lowerFreq, upperFreq);
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}

private void AcquireAnalyzerData()


{
ResourceManager rm = new ResourceManager();
FormattedIO488 io = new FormattedIO488();
io.IO =
(IMessage)rm.Open("TCPIP0::192.168.0.10::inst0::INSTR"); // Change to your
VISA address

io.WriteString("*CLS");
io.WriteString(":INIT:CONT OFF");

// Setup configuration
io.WriteString(":FREQ:CENT 1GHz");
io.WriteString(":FREQ:SPAN 10MHz");
io.WriteString(":BAND 100kHz");
io.WriteString(":BAND:VID 100kHz");

io.WriteString(":INIT; *WAI");

// Get trace data


io.WriteString(":TRAC? TRACE1");
string rawData = io.ReadString();

powerArray = rawData.Split(new[] { ',', '\n', '\r' },


StringSplitOptions.RemoveEmptyEntries)
.Select(s => double.Parse(s))
.ToArray();

// Get frequency range


io.WriteString(":FREQ:STAR?");
double startFreq = double.Parse(io.ReadString());

io.WriteString(":FREQ:STOP?");
double stopFreq = double.Parse(io.ReadString());

int pointCount = powerArray.Length;


double step = (stopFreq - startFreq) / (pointCount - 1);

freqArray = new double[pointCount];


for (int i = 0; i < pointCount; i++)
freqArray[i] = startFreq + i * step;

// Calculate OBW (99%)


double totalPower = powerArray.Sum(p => Math.Pow(10, p / 10));
double targetPower = totalPower * 0.99;

int lowIdx = 0, highIdx = pointCount - 1;


double currentSum = 0;

for (int i = 0; i < pointCount; i++)


{
for (int j = i; j < pointCount; j++)
{
currentSum = 0;
for (int k = i; k <= j; k++)
currentSum += Math.Pow(10, powerArray[k] / 10);

if (currentSum >= targetPower)


{
lowIdx = i;
highIdx = j;
goto Done;
}
}
}
Done:
lowerFreq = freqArray[lowIdx];
upperFreq = freqArray[highIdx];

io.IO.Close();
}

private void PlotSpectrum(double[] freqArray, double[] powerArray,


double lowerFreq, double upperFreq)
{
chart1.Series.Clear();
chart1.ChartAreas.Clear();

chart1.ChartAreas.Add("Main");
chart1.ChartAreas[0].AxisX.Title = "Frequency (MHz)";
chart1.ChartAreas[0].AxisY.Title = "Power (dBm)";

// Plot the trace


var trace = new Series("Spectrum");
trace.ChartType = SeriesChartType.Line;

for (int i = 0; i < freqArray.Length; i++)


trace.Points.AddXY(freqArray[i] / 1e6, powerArray[i]);

chart1.Series.Add(trace);

// OBW marker strip


var obwStrip = new StripLine
{
IntervalOffset = lowerFreq / 1e6,
StripWidth = (upperFreq - lowerFreq) / 1e6,
BackColor = Color.FromArgb(50, Color.Yellow),
Text = "99% OBW",
TextAlignment = StringAlignment.Far,
Font = new Font("Arial", 8),
BorderColor = Color.Red,
BorderWidth = 1
};

chart1.ChartAreas[0].AxisX.StripLines.Add(obwStrip);

// Markers at OBW edges


int idx1 = Array.FindIndex(freqArray, f => f >= lowerFreq);
int idx2 = Array.FindIndex(freqArray, f => f >= upperFreq);

AddMarker("OBW Start", lowerFreq / 1e6, powerArray[idx1]);


AddMarker("OBW End", upperFreq / 1e6, powerArray[idx2]);
}

private void AddMarker(string name, double freqMHz, double power)


{
var marker = new Series(name);
marker.ChartType = SeriesChartType.Point;
marker.MarkerStyle = MarkerStyle.Cross;
marker.MarkerSize = 10;
marker.Color = Color.Red;
marker.Points.AddXY(freqMHz, power);
chart1.Series.Add(marker);
}
private void btnSave_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog
{
Filter = "PNG Image|*.png",
Title = "Save Spectrum Chart",
FileName = "OBW_Spectrum.png"
};

if (sfd.ShowDialog() == DialogResult.OK)
{
chart1.SaveImage(sfd.FileName, ChartImageFormat.Png);
MessageBox.Show("Chart saved to:\n" + sfd.FileName,
"Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}

🖼 UI Summary
Element Name Purpose

Chart chart1 Displays the spectrum trace

Button btnAcquire Acquires data and plots it

Button btnSave Saves chart as PNG

🆕 Feature 1: Annotation of OBW in MHz

After calculating lowerFreq and upperFreq, we’ll calculate OBW in MHz and display it on the
chart as a text annotation.

🆕 Feature 2: Auto-Center on Peak

We'll find the frequency of the maximum power point in the trace and set the analyzer’s center
frequency to that value.

Updated Code: Modify These Sections

🔧 1. Add OBW Annotation (Right after PlotSpectrum())

Update the end of PlotSpectrum() function:

// OBW annotation
double obwMHz = (upperFreq - lowerFreq) / 1e6;
string obwText = $"99% OBW = {obwMHz:F3} MHz";

TextAnnotation annotation = new TextAnnotation


{
Text = obwText,
X = (lowerFreq + upperFreq) / 2e6,
Y = powerArray.Max(), // Put it near the top
AnchorX = (lowerFreq + upperFreq) / 2e6,
AnchorY = powerArray.Max(),
Font = new Font("Arial", 10, FontStyle.Bold),
ForeColor = Color.Blue,
Alignment = ContentAlignment.TopCenter
};
chart1.Annotations.Clear();
chart1.Annotations.Add(annotation);

2. Auto-Center on Peak Power (inside AcquireAnalyzerData())

Before you send sweep commands, find the peak index and set it as center frequency:

Replace this part:

io.WriteString(":FREQ:CENT 1GHz");

With this:

// Temporarily set a wide span to find peak

io.WriteString(":FREQ:CENT 1GHz");

io.WriteString(":FREQ:SPAN 100MHz");

io.WriteString(":INIT; *WAI");

// Get peak power

io.WriteString(":TRAC? TRACE1");

string tmpTrace = io.ReadString();

double[] tmpPowerArray = tmpTrace

.Split(new[] { ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)

.Select(s => double.Parse(s)).ToArray();

io.WriteString(":FREQ:STAR?");

double tmpStart = double.Parse(io.ReadString());

io.WriteString(":FREQ:STOP?");

double tmpStop = double.Parse(io.ReadString());


int peakIndex = Array.IndexOf(tmpPowerArray, tmpPowerArray.Max());

double freqStep = (tmpStop - tmpStart) / (tmpPowerArray.Length - 1);

double peakFreq = tmpStart + peakIndex * freqStep;

// Set new center on peak

io.WriteString($":FREQ:CENT {peakFreq}Hz");

io.WriteString(":FREQ:SPAN 10MHz"); // Narrow span after centering

💡 This will make your analyzer auto-center to the strongest signal before capturing the real OBW
measurement

Optional: Display Peak Frequency Too?

Add another annotation like

double peakPower = powerArray.Max();

int peakIndex = Array.IndexOf(powerArray, peakPower);

double peakFreqMHz = freqArray[peakIndex] / 1e6;

TextAnnotation peakNote = new TextAnnotation

Text = $"Peak @ {peakFreqMHz:F3} MHz",

X = peakFreqMHz,

Y = peakPower,

AnchorX = peakFreqMHz,

AnchorY = peakPower,

Font = new Font("Arial", 8),

ForeColor = Color.DarkGreen,

Alignment = ContentAlignment.BottomCenter
};

chart1.Annotations.Add(peakNote);

Features Included:

 ✅ Connects to Agilent E4446A


 ✅ Sets RBW, VBW, Span
 ✅ Auto-centers on peak
 ✅ Calculates Occupied Bandwidth (99%)
 ✅ Plots the spectrum
 ✅ Highlights OBW region
 ✅ Places markers at OBW edges
 ✅ Annotates OBW in MHz
 ✅ Annotates peak frequency & power
 ✅ Allows exporting chart as PNG
🧩 Instructions:

1. Create a Windows Forms App (.NET Framework) in Visual Studio.


2. Add a Chart control (chart1), and two Buttons:
o btnAcquire – labeled “Acquire”
o btnSave – labeled “Save Image”
3. Add a reference to Ivi.Visa.Interop (COM > “VISA COM 3.0 Type Library”)

✅ Complete Form1.cs Code


csharp
CopierModifier
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using Ivi.Visa.Interop;

namespace OBW_Analyzer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

double[] freqArray, powerArray;


double lowerFreq, upperFreq;

private void btnAcquire_Click(object sender, EventArgs e)


{
try
{
AcquireAnalyzerData();
PlotSpectrum(freqArray, powerArray, lowerFreq, upperFreq);
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}

private void AcquireAnalyzerData()


{
ResourceManager rm = new ResourceManager();
FormattedIO488 io = new FormattedIO488();

io.IO =
(IMessage)rm.Open("TCPIP0::192.168.0.10::inst0::INSTR"); // Replace with
your VISA address

io.WriteString("*CLS");
io.WriteString(":INIT:CONT OFF");
// Step 1: Set wide span to find peak
io.WriteString(":FREQ:CENT 1GHz");
io.WriteString(":FREQ:SPAN 100MHz");
io.WriteString(":BAND 100kHz");
io.WriteString(":BAND:VID 100kHz");
io.WriteString(":INIT; *WAI");

// Get trace to find peak


io.WriteString(":TRAC? TRACE1");
string tmpTrace = io.ReadString();
double[] tmpPowerArray = tmpTrace.Split(new[] { ',', '\n', '\r'
}, StringSplitOptions.RemoveEmptyEntries)
.Select(s =>
double.Parse(s)).ToArray();

io.WriteString(":FREQ:STAR?");
double tmpStart = double.Parse(io.ReadString());
io.WriteString(":FREQ:STOP?");
double tmpStop = double.Parse(io.ReadString());

int peakIdx = Array.IndexOf(tmpPowerArray,


tmpPowerArray.Max());
double tmpStep = (tmpStop - tmpStart) / (tmpPowerArray.Length -
1);
double peakFreq = tmpStart + peakIdx * tmpStep;

// Step 2: Center on peak, narrow span


io.WriteString($":FREQ:CENT {peakFreq}");
io.WriteString(":FREQ:SPAN 10MHz");
io.WriteString(":BAND 100kHz");
io.WriteString(":BAND:VID 100kHz");
io.WriteString(":INIT; *WAI");

// Step 3: Get final trace for OBW


io.WriteString(":TRAC? TRACE1");
string rawData = io.ReadString();
powerArray = rawData.Split(new[] { ',', '\n', '\r' },
StringSplitOptions.RemoveEmptyEntries)
.Select(s => double.Parse(s)).ToArray();

io.WriteString(":FREQ:STAR?");
double startFreq = double.Parse(io.ReadString());
io.WriteString(":FREQ:STOP?");
double stopFreq = double.Parse(io.ReadString());

int pointCount = powerArray.Length;


double step = (stopFreq - startFreq) / (pointCount - 1);
freqArray = new double[pointCount];
for (int i = 0; i < pointCount; i++)
freqArray[i] = startFreq + i * step;

// Step 4: Calculate OBW (99%)


double totalPower = powerArray.Sum(p => Math.Pow(10, p / 10));
double targetPower = totalPower * 0.99;

int lowIdx = 0, highIdx = pointCount - 1;


double currentSum = 0;

for (int i = 0; i < pointCount; i++)


{
for (int j = i; j < pointCount; j++)
{
currentSum = 0;
for (int k = i; k <= j; k++)
currentSum += Math.Pow(10, powerArray[k] / 10);

if (currentSum >= targetPower)


{
lowIdx = i;
highIdx = j;
goto Done;
}
}
}

Done:
lowerFreq = freqArray[lowIdx];
upperFreq = freqArray[highIdx];
io.IO.Close();
}

private void PlotSpectrum(double[] freqArray, double[] powerArray,


double lowerFreq, double upperFreq)
{
chart1.Series.Clear();
chart1.ChartAreas.Clear();
chart1.Annotations.Clear();

chart1.ChartAreas.Add("Main");
chart1.ChartAreas[0].AxisX.Title = "Frequency (MHz)";
chart1.ChartAreas[0].AxisY.Title = "Power (dBm)";

// Spectrum trace
var trace = new Series("Spectrum") { ChartType =
SeriesChartType.Line };
for (int i = 0; i < freqArray.Length; i++)
trace.Points.AddXY(freqArray[i] / 1e6, powerArray[i]);
chart1.Series.Add(trace);

// OBW strip
var obwStrip = new StripLine
{
IntervalOffset = lowerFreq / 1e6,
StripWidth = (upperFreq - lowerFreq) / 1e6,
BackColor = Color.FromArgb(50, Color.Yellow),
BorderColor = Color.Red,
BorderWidth = 1
};
chart1.ChartAreas[0].AxisX.StripLines.Add(obwStrip);

// OBW markers
int idx1 = Array.FindIndex(freqArray, f => f >= lowerFreq);
int idx2 = Array.FindIndex(freqArray, f => f >= upperFreq);
AddMarker("OBW Start", lowerFreq / 1e6, powerArray[idx1]);
AddMarker("OBW End", upperFreq / 1e6, powerArray[idx2]);

// OBW annotation
double obwMHz = (upperFreq - lowerFreq) / 1e6;
var obwNote = new TextAnnotation
{
Text = $"99% OBW = {obwMHz:F3} MHz",
X = (lowerFreq + upperFreq) / 2e6,
Y = powerArray.Max(),
AnchorX = (lowerFreq + upperFreq) / 2e6,
AnchorY = powerArray.Max(),
Font = new Font("Arial", 10, FontStyle.Bold),
ForeColor = Color.Blue,
Alignment = ContentAlignment.TopCenter
};
chart1.Annotations.Add(obwNote);

// Peak annotation
double peakPower = powerArray.Max();
int peakIndex = Array.IndexOf(powerArray, peakPower);
double peakFreqMHz = freqArray[peakIndex] / 1e6;

var peakNote = new TextAnnotation


{
Text = $"Peak @ {peakFreqMHz:F3} MHz",
X = peakFreqMHz,
Y = peakPower,
AnchorX = peakFreqMHz,
AnchorY = peakPower,
Font = new Font("Arial", 8),
ForeColor = Color.DarkGreen,
Alignment = ContentAlignment.BottomCenter
};
chart1.Annotations.Add(peakNote);
}

private void AddMarker(string name, double freqMHz, double power)


{
var marker = new Series(name)
{
ChartType = SeriesChartType.Point,
MarkerStyle = MarkerStyle.Cross,
MarkerSize = 10,
Color = Color.Red
};
marker.Points.AddXY(freqMHz, power);
chart1.Series.Add(marker);
}

private void btnSave_Click(object sender, EventArgs e)


{
SaveFileDialog sfd = new SaveFileDialog
{
Filter = "PNG Image|*.png",
Title = "Save Spectrum Chart",
FileName = "OBW_Spectrum.png"
};

if (sfd.ShowDialog() == DialogResult.OK)
{
chart1.SaveImage(sfd.FileName, ChartImageFormat.Png);
MessageBox.Show("Chart saved to:\n" + sfd.FileName,
"Saved", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
🎯 Recap

This single file:

 Measures signal with auto peak tracking


 Calculates and displays 99% OBW
 Highlights it in the chart with shading, markers, and

You're doing a great job calculating and visualizing the 99% occupied bandwidth. To add a
"mask" over the spectrum that matches the 99% bandwidth (or shows where the signal
should remain within), you can define a shape or limit over the frequency range of the 99%
OBW and draw it on the chart.

🛠 What does "mask" mean here?

A mask in spectrum analysis usually refers to a region (either a simple power threshold line
or a more complex envelope) that a signal must stay within. For a 99% OBW mask, you'd
typically want a shaded area over the frequency range between lowerFreq and upperFreq,
and optionally also define upper and lower power limits.

✅ Option 1: Simple Power Mask (Box Envelope)


This will draw a shaded box that shows the OBW region and allowed power range visually.

Add this inside your PlotSpectrum function, after calculating lowerFreq, upperFreq, and
plotting the trace:

// Define mask upper and lower power limits (adjust as needed)

double maskTop = powerArray.Max() + 3; // 3 dB above peak (example)

double maskBottom = powerArray.Min() - 3; // 3 dB below min (example)

// Add mask as a shaded rectangle

var maskSeries = new Series("OBW Mask")

ChartType = SeriesChartType.Area,

Color = Color.FromArgb(50, Color.Red),

BorderWidth = 0

};

// Add points to define the rectangular mask

maskSeries.Points.AddXY(lowerFreq / 1e6, maskBottom);

maskSeries.Points.AddXY(lowerFreq / 1e6, maskTop);

maskSeries.Points.AddXY(upperFreq / 1e6, maskTop);


maskSeries.Points.AddXY(upperFreq / 1e6, maskBottom);

maskSeries.Points.AddXY(lowerFreq / 1e6, maskBottom); // Close the polygon

chart1.Series.Add(maskSeries);

✅ Option 2: Mask as Line Limits (Horizontal power limits)


To just add upper/lower power limits as lines within OBW:

var upperMaskLine = new StripLine

IntervalOffset = maskTop,

BorderColor = Color.Red,

BorderWidth = 1,

Text = "Upper Limit",

TextAlignment = StringAlignment.Far,

Font = new Font("Arial", 8),

ForeColor = Color.Red

};

chart1.ChartAreas[0].AxisY.StripLines.Add(upperMaskLine);

var lowerMaskLine = new StripLine

IntervalOffset = maskBottom,

BorderColor = Color.Red,

BorderWidth = 1,

Text = "Lower Limit",

TextAlignment = StringAlignment.Near,
Font = new Font("Arial", 8),

ForeColor = Color.Red

};

chart1.ChartAreas[0].AxisY.StripLines.Add(lowerMaskLine);

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy