Impulse response plot for IIF filters

Optional IIF filter impulse response plot.
This commit is contained in:
Douglas Gillespie 2025-03-03 00:57:54 +00:00
parent 7b6466b06f
commit cadf61d218
2 changed files with 158 additions and 5 deletions

View File

@ -5,6 +5,7 @@ import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
@ -18,6 +19,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.LineMetrics;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
@ -29,10 +31,12 @@ import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
@ -42,12 +46,15 @@ import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.table.AbstractTableModel;
import org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf.PDArtifactMarkedContent;
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;
import Layout.PamAxis;
import PamUtils.PamFileChooser;
import PamUtils.PamFileFilter;
import PamUtils.PamUtils;
import PamView.PamColors;
import PamView.PamColors.PamColor;
import PamView.dialog.PamDialog;
@ -56,8 +63,8 @@ import PamView.panel.PamPanel;
import fftManager.Complex;
/**
* Create a more generic dialog panel for the PAMGurd
* filters which can be incorporated into larger pnales
* Create a more generic dialog panel for the PAMGuard
* filters which can be incorporated into larger panels
* if desired.
* @author Doug Gillespie
*
@ -696,6 +703,57 @@ public class FilterDialogPanel implements ActionListener {
// PamColors.getInstance().registerComponent(this,
// PamColors.PamColor.PlOTWINDOW);
setPreferredSize(new Dimension(100,200));
addMouseListener(new PZMouse());
setToolTipText("Right click to change IIR Filter plot type");
}
private class PZMouse extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
showPopupMenu(e);
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
showPopupMenu(e);
}
}
}
private void showPopupMenu(MouseEvent e) {
if (filterMethod == null) {
return;
}
if (FIRFilterMethod.class.isAssignableFrom(filterMethod.getClass())) {
return;
}
JPopupMenu popMenu = new JPopupMenu();
JCheckBoxMenuItem menuItem;
menuItem = new JCheckBoxMenuItem("Show Pole-Zero plot", FilterParams.pzPlotStyle == FilterParams.PZPLOT_PZ);
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
FilterParams.pzPlotStyle = FilterParams.PZPLOT_PZ;
repaint();
}
});
popMenu.add(menuItem);
menuItem = new JCheckBoxMenuItem("Show Impulse Response", FilterParams.pzPlotStyle == FilterParams.PZPLOT_IMPULSE);
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
FilterParams.pzPlotStyle = FilterParams.PZPLOT_IMPULSE;
repaint();
}
});
popMenu.add(menuItem);
popMenu.show(e.getComponent(), e.getX(), e.getY());
}
@Override
@ -705,8 +763,12 @@ public class FilterDialogPanel implements ActionListener {
return;
}
if (IIRFilterMethod.class.isAssignableFrom(filterMethod.getClass())) {
paintIIRImpulseResponse(g);
paintPoleZeros(g);
if (FilterParams.pzPlotStyle == FilterParams.PZPLOT_IMPULSE) {
paintIIRImpulseResponse(g);
}
else {
paintPoleZeros(g);
}
}
else if (FIRFilterMethod.class.isAssignableFrom(filterMethod.getClass())) {
paintImpulseResponse(g);
@ -781,6 +843,9 @@ public class FilterDialogPanel implements ActionListener {
for (int i = 0; i < iirFilterMethod.poleZeroCount(); i++) {
drawZero(g, zeros[i], center, radius);
}
g.setColor(Color.BLACK);
String txt = "Pole-Zero";
cornerText(g, txt);
}
/**
@ -795,6 +860,20 @@ public class FilterDialogPanel implements ActionListener {
if (filterMethod == null) {
return;
}
// try to do an upsampled filter method to make the plot clearer
int upsFactor = 10;
FilterParams upFP = iirFilterMethod.filterParams.clone();
IIRFilterMethod upsMethod = (IIRFilterMethod) FilterMethod.createFilterMethod(sampleRate*upsFactor, upFP);
int upsPoints = upsMethod.filterParams.filterOrder*10*upsFactor;
double[] upsInput = new double[upsPoints];
double[] upsOutput = new double[upsPoints];
Filter upsFilter = upsMethod.createFilter(0);
for (int i = 0; i < upsFactor; i++) {
upsInput[i] = 1;
}
// upsInput[0] = upsFactor;
upsFilter.runFilter(upsInput, upsOutput);
int nPoints = iirFilterMethod.filterParams.filterOrder * 10;
Filter filter = iirFilterMethod.createFilter(0);
filter.prepareFilter();
@ -802,15 +881,85 @@ public class FilterDialogPanel implements ActionListener {
double[] output = new double[nPoints];
input[0] = 1;
filter.runFilter(input, output);
double maxOut = 0;
for (int i = 0; i < output.length; i++) {
maxOut = Math.max(maxOut, Math.abs(output[i]));
}
double[] scaleMaxes = {1, .5, .2, .1};
double scaleMax = 1.0;
for (int i = 1; i < scaleMaxes.length; i++) {
if (maxOut > scaleMaxes[i]) {
break;
}
scaleMax = scaleMaxes[i];
}
// can now plot that.
// what's the lenght of the plot in seconds ?
double tSecs = (nPoints-1)/sampleRate;
double tScale = 1;
String tUnit = "s";
if (tSecs < .1) {
tScale = 1000;
tUnit = "ms";
}
if (tSecs < 1e-4) {
tScale = 1e6;
tUnit = "\u00B5s";
}
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(PamColors.getInstance().getColor(PamColor.AXIS));
int charWidth = g2d.getFontMetrics().charWidth('2');
Insets insets = getInsets();
Rectangle r = getBounds();
int marginL = charWidth*3+6;
int marginT = 6;
int marginR = charWidth*2+3;
int x0 = marginL;
int y0 = getHeight()/2;
int y1 = getWidth()-marginR;
double yScale = getHeight()/2-marginT;
g.setColor(Color.BLACK);
g.drawLine(x0, getHeight()-marginT, x0, marginT);
PamAxis yAxis = new PamAxis(x0, getHeight()-marginT, x0, marginT, -1, 1, PamAxis.ABOVE_LEFT, "", PamAxis.LABEL_NEAR_CENTRE, "%3.1f");
yAxis.drawAxis(g);
g.drawLine(x0, y0, y1, y0);
PamAxis xAxis = new PamAxis(x0, y0, y1, y0, 0, tSecs*tScale, PamAxis.BELOW_RIGHT, tUnit, PamAxis.LABEL_NEAR_MAX, "%3.1f");
xAxis.drawAxis(g);
int lastX = -1;
int lastY = 0;
g.setColor(Color.GRAY);
for (int i = upsFactor; i < upsInput.length; i++) {
int x = (int) xAxis.getPosition((i-upsFactor)/sampleRate/upsFactor*tScale) + marginL;
int y = (int) yAxis.getPosition(upsOutput[i]) + marginT;
if (i > upsFactor) {
g2d.drawLine(lastX, lastY, x, y);
}
lastX = x;
lastY = y;
}
g.setColor(Color.RED);
for (int i = 0; i < input.length; i++) {
int x = (int) xAxis.getPosition(i/sampleRate*tScale) + marginL;
int y = (int) yAxis.getPosition(output[i]) + marginT;
if (i > 0) {
g2d.drawLine(lastX, lastY, x, y);
}
lastX = x;
lastY = y;
}
g.setColor(Color.BLACK);
String txt = "Impulse response";
cornerText(g, txt);
}
void cornerText(Graphics g, String txt) {
FontMetrics fm = g.getFontMetrics();
int w = fm.stringWidth(txt);
g.drawString(txt, getWidth()-w-fm.getMaxAdvance(), fm.getAscent()*3/2);
}
void drawPole(Graphics g, Complex p, Point center, int radius) {

View File

@ -75,6 +75,10 @@ public class FilterParams implements Serializable, Cloneable, ManagedParameters
public static final int SCALE_LOG = 0;
public static final int SCALE_LIN = 1;
public static final int PZPLOT_PZ = 0;
public static final int PZPLOT_IMPULSE = 1;
public static int pzPlotStyle = PZPLOT_IMPULSE;
/**
* Construct a filter parameter set with default params
*/