// // Huff & Puff VFO Stabiliser Freqency model // import java.awt.*; import java.util.*; import java.applet.Applet; public class huffpufffreq extends Applet { Panel plotPanel, panel1, panelL, panelR, statusPanel; Label statusLabel; TextField fldVfoFreq, fldCrystalFreq, fldDivisionStages, fldDelayStages, fldCorrectionRate, fldDriftRate; TextField fldMarkSpaceRatio, fldGraphHeight, fldSamples, fldMeasSamples; graphcanvas graph; Label lblStep, lblFinalFreq, lblRipple; double vfoFreq = 5.0; double crystalFreq = 32.0; int divisionStages = 8; int delayStages = 256; int samples = 2000; int measSamples = 400; double correctionRate = 300.0; double driftRate = 100.0; double markSpaceRatio = 50.0; double graphHeight = 1.0; private TextField addField(String lbl, String dflt) { TextField fld; panelL.add(new Label(lbl)); fld = new TextField(dflt, 7); panelR.add(fld); return(fld); } private Label addLabel(String lbl) { Label label; panelL.add(new Label(lbl)); label = new Label(""); panelR.add(label); return(label); } public void init() { setLayout(new BorderLayout(5,5)); panel1 = new Panel(); panelL = new Panel(); panelL.setLayout(new GridLayout(14, 1)); panel1.add(panelL); panelR = new Panel(); panelR.setLayout(new GridLayout(14, 1)); panel1.add(panelR); fldVfoFreq = addField("VFO Freq (MHz) : ", new Long(Math.round(vfoFreq)).toString()); fldCrystalFreq = addField("Crystal Freq (MHz) : ", new Long(Math.round(crystalFreq)).toString()); fldDivisionStages = addField("VFO Division Stages : ", new Integer(divisionStages).toString()); fldDelayStages = addField("Delay Stages : ", new Integer(delayStages).toString()); fldCorrectionRate = addField("Correction Rate (Hz/s) : ", new Long(Math.round(correctionRate)).toString()); fldDriftRate = addField("Drift Rate (Hz/s) : ", new Long(Math.round(driftRate)).toString()); fldMarkSpaceRatio = addField("Mark/Space Ratio (%) : ", new Long(Math.round(markSpaceRatio)).toString()); fldGraphHeight = addField("Graph Height (Steps) : ", new Long(Math.round(graphHeight)).toString()); fldSamples = addField("Number of Samples : ", new Long(Math.round(samples)).toString()); fldMeasSamples = addField("Measurement Samples : ", new Long(Math.round(measSamples)).toString()); addLabel(""); lblStep = addLabel("Step (Hz) : "); lblFinalFreq = addLabel("Measured Final Freq (Hz) : "); lblRipple = addLabel("Measured Ripple (Hz) : "); add("West", panel1); graph = new graphcanvas(); add("Center", graph); statusPanel = new Panel(); // Status Line statusPanel.add(new Label("Status: ")); statusLabel = new Label("OK "); statusPanel.add(statusLabel); add("South", statusPanel); } public void paint(Graphics g) { Dimension d = getSize(); g.drawRect(0,0, d.width - 1, d.height - 1); } public Insets insets() { return new Insets(5,5,5,8); } public double getDblFromField(TextField fld, String errText) { double d; d = 0.0; try { d = Float.valueOf(fld.getText()).floatValue(); if (d == 0.0) { statusLabel.setText("Enter a non-zero value for " + errText); } } catch (NumberFormatException el) { statusLabel.setText("Enter a numeric value for " + errText); } return d; } public int getIntFromField(TextField fld, String errText) { int i; i = 0; try { i = Integer.valueOf(fld.getText()).intValue(); if (i == 0.0) { statusLabel.setText("Enter a non-zero value for " + errText); } } catch (NumberFormatException el) { statusLabel.setText("Enter an integral value for " + errText); } return i; } public void simulate() { statusLabel.setText("OK"); vfoFreq = getDblFromField(fldVfoFreq, "VFO Frequency!"); crystalFreq = getDblFromField(fldCrystalFreq, "Crystal Frequecy!"); divisionStages = getIntFromField(fldDivisionStages, "Number of VFO Division Stages!"); delayStages = getIntFromField(fldDelayStages, "Number of Delay Stages!"); correctionRate = getDblFromField(fldCorrectionRate, "Correction Rate!"); driftRate = getDblFromField(fldDriftRate, "Drift Rate!"); markSpaceRatio = getDblFromField(fldMarkSpaceRatio, "Mark/Space Ratio!"); graphHeight = getDblFromField(fldGraphHeight, "Graph Height!"); samples = getIntFromField(fldSamples, "Number of Samples!"); measSamples = getIntFromField(fldMeasSamples, "Number of Maesurement Samples!"); repaint(); graph.repaint(); } public boolean handleEvent(Event e) { if (e.id == Event.ACTION_EVENT) { simulate(); } return false; } class graphcanvas extends Canvas { private String dblToOneDp(double d) { return(new Long(Math.round(Math.floor(d))).toString() + "." + new Long(Math.round(10.0 * (d - Math.floor(d)))).toString()); } private String dblToThreeDp(double d) { return(new Double(Math.round(d * 1000.0) / 1000.0).toString()); } public void paint (Graphics g) { double f0; double crystal; double step; double vfoDivisor, tankMultiplier, oneCorrection; int xpos, ypos, xposNew, yposNew, yposCentre; double xScale, yScale; int i; double t, input, drift, correction, tank, freq; boolean xoState, delayed, xor; boolean[] register = new boolean[samples]; double minFreq, maxFreq, avgFreq; Dimension d = getSize(); g.setColor(Color.blue); g.drawRect(0, 0, d.width - 1, d.height - 1); f0 = vfoFreq * 1000000.0; crystal = crystalFreq * 1000000.0; vfoDivisor = Math.pow(2, divisionStages); step = f0 * f0 / (vfoDivisor * delayStages * crystal); tankMultiplier = f0 * 0.5 / 10000.0; oneCorrection = (vfoDivisor * correctionRate) / (f0 * tankMultiplier); yposCentre = d.height / 2; xpos = 0; ypos = yposCentre; xScale = d.width / (samples * vfoDivisor / f0); yScale = d.height / (step * graphHeight); lblStep.setText(dblToOneDp(step)); g.drawLine(0, d.height / 2, 4, d.height / 2); g.drawString(dblToOneDp(f0), 5, d.height / 2); g.drawString(dblToOneDp(f0 + step * graphHeight / 2.0), 5, 15); g.drawString(dblToOneDp(f0 - step * graphHeight / 2.0), 5, d.height - 7); t = 0.0; input = 5.0; freq = f0; minFreq = 1e99; maxFreq = 0.0; avgFreq = 0.0; drift = vfoDivisor * driftRate / (f0 * tankMultiplier); for (i = 0; i < samples; i += 1) register[i] = false; // Initialise shift register for (i = 0; i < samples; i += 1) { xposNew = new Double(t * xScale).intValue(); yposNew = new Double(yposCentre + (f0 - freq) * yScale).intValue(); g.drawLine(xpos, ypos, xposNew, yposNew); xpos = xposNew; ypos = yposNew; xoState = (t * crystal - Math.floor(t * crystal)) > (markSpaceRatio / 100.0); register[i] = xoState; if (i < delayStages) delayed = false; else delayed = register[i - delayStages]; xor = (xoState && !delayed) || (!xoState && delayed); if (xor) correction = oneCorrection; else correction = -oneCorrection; tank = input + drift + correction; freq = f0 + (tank - 5.0) * tankMultiplier; t += vfoDivisor / freq; input = tank; if (i >= samples - measSamples) { minFreq = Math.min(minFreq, freq); maxFreq = Math.max(maxFreq, freq); avgFreq = avgFreq + freq; } } lblFinalFreq.setText(dblToOneDp(avgFreq / measSamples)); lblRipple.setText(dblToThreeDp(maxFreq - minFreq)); } } } /* End of class */