- {
- @Override
- public void receive(SourceEvent sourceEvent)
- {
- if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_FREQUENCY_CHANGE)
- {
- mChannelDescriptor = new StandardChannel(sourceEvent.getValue().longValue());
- }
- }
+ StringBuilder sb = new StringBuilder();
+ sb.append("Activity Summary\n");
+ sb.append("\tDecoder: NBFM");
+ sb.append("\n\n");
+ return sb.toString();
}
}
diff --git a/src/main/java/io/github/dsheirer/module/demodulate/am/AMDemodulatorModule.java b/src/main/java/io/github/dsheirer/module/demodulate/am/AMDemodulatorModule.java
deleted file mode 100644
index cd400b5c..00000000
--- a/src/main/java/io/github/dsheirer/module/demodulate/am/AMDemodulatorModule.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
- *
- * 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 3 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, see
- * ****************************************************************************
- */
-package io.github.dsheirer.module.demodulate.am;
-
-import io.github.dsheirer.dsp.am.AMDemodulator;
-import io.github.dsheirer.dsp.filter.FilterFactory;
-import io.github.dsheirer.dsp.filter.design.FilterDesignException;
-import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
-import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
-import io.github.dsheirer.dsp.filter.resample.RealResampler;
-import io.github.dsheirer.dsp.gain.AutomaticGainControl;
-import io.github.dsheirer.dsp.window.WindowType;
-import io.github.dsheirer.module.Module;
-import io.github.dsheirer.sample.Listener;
-import io.github.dsheirer.sample.complex.ComplexSamples;
-import io.github.dsheirer.sample.complex.IComplexSamplesListener;
-import io.github.dsheirer.sample.real.IRealBufferProvider;
-import io.github.dsheirer.source.ISourceEventListener;
-import io.github.dsheirer.source.SourceEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * AM Demodulator Module for demodulating complex baseband channel sample buffers and producing demodulated audio
- * at a specified output sample rate, normally 8 kHz.
- *
- * This module requires a sample rate SourceEvent prior to processing baseband sample buffers in order to configure
- * internal filters and the resampler.
- */
-public class AMDemodulatorModule extends Module
- implements ISourceEventListener, IComplexSamplesListener, IRealBufferProvider, Listener
-{
- private final static Logger mLog = LoggerFactory.getLogger(AMDemodulatorModule.class);
- private IRealFilter mIBasebandFilter;
- private IRealFilter mQBasebandFilter;
-
- private AMDemodulator mDemodulator;
- private IRealFilter mLowPassFilter;
- private AutomaticGainControl mAGC = new AutomaticGainControl();
- private double mChannelBandwidth;
- private double mOutputSampleRate;
- private RealResampler mResampler;
- private Listener mResampledReusableBufferListener;
- private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
-
- /**
- * Constructs an AM demodulator module
- *
- * @param channelBandwidth to use in filtering the baseband input buffers
- * @param outputSampleRate specifies the resampled output sample rate for demodulated audio
- */
- public AMDemodulatorModule(double channelBandwidth, double outputSampleRate)
- {
- mChannelBandwidth = channelBandwidth;
- mOutputSampleRate = outputSampleRate;
-
- mDemodulator = new AMDemodulator(500.0f);
-
- float[] lowPassCoefficients = null;
-
- FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder().sampleRate(mOutputSampleRate)
- .gridDensity(16).oddLength(true).passBandCutoff(3000).passBandAmplitude(1.0).passBandRipple(0.01)
- .stopBandStart(3500).stopBandAmplitude(0.0).stopBandRipple(0.028).build();
-
- try
- {
- lowPassCoefficients = FilterFactory.getTaps(specification);
- }
- catch(FilterDesignException fde)
- {
- mLog.error("Couldn't design AM demodulator remez filter for sample rate [" + mOutputSampleRate
- + "] pass frequency [3000] and stop frequency [3500] - using sinc filter");
- }
-
- if(lowPassCoefficients == null)
- {
- lowPassCoefficients = FilterFactory.getLowPass(mOutputSampleRate, 3000, 3500, 60, WindowType.HAMMING, true);
- }
-
- mLowPassFilter = FilterFactory.getRealFilter(lowPassCoefficients);
- }
-
- @Override public void receive(ComplexSamples samples)
- {
- float[] i = mIBasebandFilter.filter(samples.i());
- float[] q = mQBasebandFilter.filter(samples.q());
- float[] demodulated = mDemodulator.demodulate(i, q);
- mResampler.resample(demodulated);
- }
-
- /**
- * Processes resampled audio buffers produced by the resampler.
- *
- * @param resampledAudio buffer to process.
- */
- private void processResampledAudio(float[] resampledAudio)
- {
- if(mResampledReusableBufferListener != null)
- {
- float[] filtered = mLowPassFilter.filter(resampledAudio);
- float[] amplified = mAGC.process(filtered);
- mResampledReusableBufferListener.receive(amplified);
- }
- }
-
- @Override public Listener getComplexSamplesListener()
- {
- return this;
- }
-
- @Override public void reset()
- {
- }
-
- @Override public void start()
- {
- }
-
- @Override public void stop()
- {
- }
-
- @Override public void setBufferListener(Listener listener)
- {
- mResampledReusableBufferListener = listener;
- }
-
- @Override public void removeBufferListener()
- {
- mResampledReusableBufferListener = null;
- }
-
- @Override public Listener getSourceEventListener()
- {
- return mSourceEventProcessor;
- }
-
- /**
- * Monitors sample rate change source event(s) to setup the initial I/Q filter and resampler
- */
- public class SourceEventProcessor implements Listener
- {
- @Override public void receive(SourceEvent sourceEvent)
- {
- if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE)
- {
- if(mIBasebandFilter != null || mQBasebandFilter != null)
- {
- mIBasebandFilter = null;
- mQBasebandFilter = null;
- }
-
- double sampleRate = sourceEvent.getValue().doubleValue();
-
- if((sampleRate < (2.0 * mChannelBandwidth)))
- {
- throw new IllegalStateException("AM Demodulator with channel bandwidth [" + mChannelBandwidth
- + "] requires a channel sample rate of [" + (2.0 * mChannelBandwidth
- + "] - sample rate of [" + sampleRate + "] is not supported"));
- }
-
- double cutoff = sampleRate / 4.0;
- int passBandStop = (int)cutoff - 500;
- int stopBandStart = (int)cutoff + 500;
-
- float[] coefficients = null;
-
- FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder().sampleRate(sampleRate)
- .gridDensity(16).oddLength(true).passBandCutoff(passBandStop).passBandAmplitude(1.0)
- .passBandRipple(0.01).stopBandStart(stopBandStart).stopBandAmplitude(0.0)
- .stopBandRipple(0.028) //Approximately 60 dB attenuation
- .build();
-
- try
- {
- coefficients = FilterFactory.getTaps(specification);
- }
- catch(FilterDesignException fde)
- {
- mLog.error("Couldn't design AM demodulator remez filter for sample rate [" + sampleRate
- + "] pass frequency [" + passBandStop + "] and stop frequency [" + stopBandStart
- + "] - using sinc filter");
- }
-
- if(coefficients == null)
- {
- coefficients = FilterFactory.getLowPass(sampleRate, passBandStop, stopBandStart, 60,
- WindowType.HAMMING, true);
- }
-
- mIBasebandFilter = FilterFactory.getRealFilter(coefficients);
- mQBasebandFilter = FilterFactory.getRealFilter(coefficients);
- mResampler = new RealResampler(sampleRate, mOutputSampleRate, 8192, 512);
- mResampler.setListener(resampledBuffer -> processResampledAudio(resampledBuffer));
- }
- }
- }
-}
diff --git a/src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java b/src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java
index 362232d4..d7af75a6 100644
--- a/src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java
+++ b/src/main/java/io/github/dsheirer/preference/identifier/TalkgroupFormatPreference.java
@@ -1,23 +1,20 @@
/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
- * * ******************************************************************************
- * * Copyright (C) 2014-2019 Dennis Sheirer
- * *
- * * 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 3 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, see
- * * *****************************************************************************
+ * 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 3 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, see
+ * ****************************************************************************
*/
package io.github.dsheirer.preference.identifier;
@@ -29,6 +26,7 @@ import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.preference.Preference;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
+import io.github.dsheirer.preference.identifier.talkgroup.AnalogTalkgroupFormatter;
import io.github.dsheirer.preference.identifier.talkgroup.DMRTalkgroupFormatter;
import io.github.dsheirer.preference.identifier.talkgroup.FleetsyncTalkgroupFormatter;
import io.github.dsheirer.preference.identifier.talkgroup.LTRTalkgroupFormatter;
@@ -37,13 +35,12 @@ import io.github.dsheirer.preference.identifier.talkgroup.MPT1327TalkgroupFormat
import io.github.dsheirer.preference.identifier.talkgroup.PassportTalkgroupFormatter;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.sample.Listener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import java.util.prefs.Preferences;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Specifies and applies user preferences for formatting identifiers
@@ -149,6 +146,7 @@ public class TalkgroupFormatPreference extends Preference
case LTR_NET:
case MPT1327:
return IntegerFormat.FORMATTED;
+ case AM:
case APCO25:
case DMR:
case MDC1200:
@@ -169,6 +167,7 @@ public class TalkgroupFormatPreference extends Preference
case LTR_NET:
case MPT1327:
return IntegerFormat.DECIMAL_FORMATTED;
+ case AM:
case APCO25:
case DMR:
case MDC1200:
@@ -187,6 +186,7 @@ public class TalkgroupFormatPreference extends Preference
case MDC1200:
case PASSPORT:
return false;
+ case AM:
case APCO25:
case DMR:
case FLEETSYNC:
@@ -314,6 +314,9 @@ public class TalkgroupFormatPreference extends Preference
{
switch(talkgroupIdentifier.getProtocol())
{
+ case AM:
+ return AnalogTalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.AM),
+ isTalkgroupFixedWidth(Protocol.AM));
case APCO25:
return APCO25TalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.APCO25),
isTalkgroupFixedWidth(Protocol.APCO25));
@@ -332,6 +335,9 @@ public class TalkgroupFormatPreference extends Preference
case MPT1327:
return MPT1327TalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.MPT1327),
isTalkgroupFixedWidth(Protocol.MPT1327));
+ case NBFM:
+ return AnalogTalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.NBFM),
+ isTalkgroupFixedWidth(Protocol.NBFM));
case PASSPORT:
return PassportTalkgroupFormatter.format(talkgroupIdentifier, getTalkgroupFormat(Protocol.PASSPORT),
isTalkgroupFixedWidth(Protocol.PASSPORT));
diff --git a/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/NBFMTalkgroupFormatter.java b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/AnalogTalkgroupFormatter.java
similarity index 94%
rename from src/main/java/io/github/dsheirer/preference/identifier/talkgroup/NBFMTalkgroupFormatter.java
rename to src/main/java/io/github/dsheirer/preference/identifier/talkgroup/AnalogTalkgroupFormatter.java
index 2535c156..31ca2bd1 100644
--- a/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/NBFMTalkgroupFormatter.java
+++ b/src/main/java/io/github/dsheirer/preference/identifier/talkgroup/AnalogTalkgroupFormatter.java
@@ -1,7 +1,6 @@
/*
- * ******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2019 Dennis Sheirer
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* 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
@@ -15,7 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.preference.identifier.talkgroup;
@@ -23,7 +22,7 @@ package io.github.dsheirer.preference.identifier.talkgroup;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.preference.identifier.IntegerFormat;
-public class NBFMTalkgroupFormatter extends AbstractIntegerFormatter
+public class AnalogTalkgroupFormatter extends AbstractIntegerFormatter
{
public static final int DECIMAL_WIDTH = 5;
public static final int HEXADECIMAL_WIDTH = 4;
diff --git a/src/main/java/io/github/dsheirer/protocol/Protocol.java b/src/main/java/io/github/dsheirer/protocol/Protocol.java
index 99612938..8e4c6950 100644
--- a/src/main/java/io/github/dsheirer/protocol/Protocol.java
+++ b/src/main/java/io/github/dsheirer/protocol/Protocol.java
@@ -1,23 +1,20 @@
/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
- * * ******************************************************************************
- * * Copyright (C) 2014-2019 Dennis Sheirer
- * *
- * * 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 3 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, see
- * * *****************************************************************************
+ * 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 3 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, see
+ * ****************************************************************************
*/
package io.github.dsheirer.protocol;
@@ -28,6 +25,7 @@ import java.util.EnumSet;
*/
public enum Protocol
{
+ AM("AM", "AM", 0),
APCO25("APCO-25", "APCO25PHASE1", 9600),
APCO25_PHASE2("APCO-25 P2", "APCO25PHASE2", 12000),
ARS("ARS", "ARS", 0),
@@ -51,7 +49,6 @@ public enum Protocol
private String mFileNameLabel;
private int mBitRate;
-
Protocol(String label, String fileNameLabel, int bitRate)
{
mLabel = label;
@@ -59,7 +56,7 @@ public enum Protocol
mBitRate = bitRate;
}
- public static EnumSet TALKGROUP_PROTOCOLS = EnumSet.of(APCO25, DMR, FLEETSYNC, LTR, LTR_NET, MDC1200,
+ public static EnumSet TALKGROUP_PROTOCOLS = EnumSet.of(AM, APCO25, DMR, FLEETSYNC, LTR, LTR_NET, MDC1200,
MPT1327, NBFM, PASSPORT);
public static EnumSet RADIO_ID_PROTOCOLS = EnumSet.of(APCO25, DMR, PASSPORT);
diff --git a/src/main/java/io/github/dsheirer/source/SourceEvent.java b/src/main/java/io/github/dsheirer/source/SourceEvent.java
index 96c686b9..2254b216 100644
--- a/src/main/java/io/github/dsheirer/source/SourceEvent.java
+++ b/src/main/java/io/github/dsheirer/source/SourceEvent.java
@@ -45,6 +45,7 @@ public class SourceEvent
NOTIFICATION_RECORDING_FILE_LOADED,
NOTIFICATION_SAMPLE_RATE_CHANGE,
NOTIFICATION_SQUELCH_THRESHOLD,
+ NOTIFICATION_SQUELCH_AUTO_TRACK,
NOTIFICATION_STOP_SAMPLE_STREAM,
NOTIFICATION_TUNER_SHUTDOWN,
NOTIFICATION_ERROR_STATE,
@@ -54,6 +55,8 @@ public class SourceEvent
REQUEST_FREQUENCY_SELECTION,
REQUEST_CHANGE_SQUELCH_THRESHOLD,
REQUEST_CURRENT_SQUELCH_THRESHOLD,
+ REQUEST_CHANGE_SQUELCH_AUTO_TRACK,
+ REQUEST_CURRENT_SQUELCH_AUTO_TRACK,
REQUEST_START_SAMPLE_STREAM,
REQUEST_STOP_SAMPLE_STREAM;
@@ -428,12 +431,11 @@ public class SourceEvent
/**
* Requests the current squelch threshold value
- * @param source optional
* @return source event
*/
- public static SourceEvent requestCurrentSquelchThreshold(Source source)
+ public static SourceEvent requestCurrentSquelchThreshold()
{
- return new SourceEvent(Event.REQUEST_CURRENT_SQUELCH_THRESHOLD, source, 0);
+ return new SourceEvent(Event.REQUEST_CURRENT_SQUELCH_THRESHOLD, null, 0);
}
/**
@@ -447,6 +449,35 @@ public class SourceEvent
return new SourceEvent(Event.REQUEST_CHANGE_SQUELCH_THRESHOLD, source, thresholdDb);
}
+ /**
+ * Creates a new request squelch auto track status source event.
+ * @return new source event
+ */
+ public static SourceEvent requestCurrentSquelchAutoTrack()
+ {
+ return new SourceEvent(Event.REQUEST_CURRENT_SQUELCH_AUTO_TRACK, null, 0);
+ }
+
+ /**
+ * Creates a new squelch auto-track notification
+ * @param autoTrack true or false.
+ * @return source event
+ */
+ public static SourceEvent squelchAutoTrack(boolean autoTrack)
+ {
+ return new SourceEvent(Event.NOTIFICATION_SQUELCH_AUTO_TRACK, null, autoTrack ? 1 : 0);
+ }
+
+ /**
+ * Creates a new squelch auto-track request
+ * @param autoTrack to enable (true) or disable auto-track.
+ * @return source event
+ */
+ public static SourceEvent requestSquelchAutoTrack(boolean autoTrack)
+ {
+ return new SourceEvent(Event.REQUEST_CHANGE_SQUELCH_AUTO_TRACK, null, autoTrack ? 1 : 0);
+ }
+
@Override
public String toString()
{
diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelSpecification.java b/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelSpecification.java
index 941f0c6c..29dbd3bf 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelSpecification.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/channel/ChannelSpecification.java
@@ -1,18 +1,21 @@
-/*******************************************************************************
- * sdr-trunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
- * 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 3 of the License, or (at your option) any
- * later version.
+ * 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 3 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.
+ * 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, see
- *
- ******************************************************************************/
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
package io.github.dsheirer.source.tuner.channel;
/**
@@ -29,8 +32,8 @@ public class ChannelSpecification
* Constructs a channel specification instance.
* @param minimumSampleRate requested for the channel
* @param bandwidth
- * @param passFrequency for the pass band of the channel
- * @param stopFrequency for the stop band of teh channel
+ * @param passFrequency for the pass band of the channel, usually half of the bandwidth value.
+ * @param stopFrequency for the stop band of teh channel, usually slightly more than half of the bandwidth value.
*/
public ChannelSpecification(double minimumSampleRate, int bandwidth, double passFrequency, double stopFrequency)
{
diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java
index ee2fd464..dc2e5ff0 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java
@@ -164,8 +164,11 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
case NOTIFICATION_FREQUENCY_ROTATION_SUCCESS:
case NOTIFICATION_CHANNEL_POWER:
case NOTIFICATION_SQUELCH_THRESHOLD:
+ case NOTIFICATION_SQUELCH_AUTO_TRACK:
case REQUEST_CHANGE_SQUELCH_THRESHOLD:
case REQUEST_CURRENT_SQUELCH_THRESHOLD:
+ case REQUEST_CHANGE_SQUELCH_AUTO_TRACK:
+ case REQUEST_CURRENT_SQUELCH_AUTO_TRACK:
//Ignore
break;
default:
diff --git a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java
index d6d74a99..82bedb03 100644
--- a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java
+++ b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* 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
@@ -47,6 +47,7 @@ public class ComplexWaveSource extends Source implements IControllableFileSource
private final static Logger mLog = LoggerFactory.getLogger(ComplexWaveSource.class);
private IFrameLocationListener mFrameLocationListener;
+ private int mBufferSampleCount = 65536; //Complex samples per buffer
private int mBytesPerFrame;
private int mFrameCounter = 0;
private long mFrequency = 0;
@@ -119,7 +120,8 @@ public class ComplexWaveSource extends Source implements IControllableFileSource
*/
public int getBufferSampleCount()
{
- return (int)(getSampleRate() / 20.0d);
+ return mBufferSampleCount;
+// return (int)(getSampleRate() / 20.0d);
}
@Override
@@ -139,12 +141,11 @@ public class ComplexWaveSource extends Source implements IControllableFileSource
if(mAutoReplay)
{
- long intervalMilliseconds = 50; //20 intervals per second
- double framesPerInterval = getSampleRate() / 20.0d;
- mReplayController = ThreadPool.SCHEDULED.scheduleAtFixedRate(new ReplayController(framesPerInterval),
+ double sampleRate = getSampleRate();
+ double buffersPerSecond = (sampleRate / mBufferSampleCount);
+ long intervalMilliseconds = (long)(1000.0 / buffersPerSecond);
+ mReplayController = ThreadPool.SCHEDULED.scheduleAtFixedRate(new ReplayController(mBufferSampleCount),
0, intervalMilliseconds, TimeUnit.MILLISECONDS);
-
-
}
}
diff --git a/src/main/java/io/github/dsheirer/vector/VectorUtilities.java b/src/main/java/io/github/dsheirer/vector/VectorUtilities.java
index 05b459d5..3c6099b4 100644
--- a/src/main/java/io/github/dsheirer/vector/VectorUtilities.java
+++ b/src/main/java/io/github/dsheirer/vector/VectorUtilities.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* 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
@@ -63,8 +63,22 @@ public class VectorUtilities
{
if(i.length % species.length() != 0)
{
- throw new IllegalArgumentException("I/Q buffer lengths [" + i.length + "] must be a power of 2 multiple of SIMD lane width [" +
- species.length() + "]");
+ throw new IllegalArgumentException("I/Q buffer lengths [" + i.length + "] must be a power of 2 multiple of " +
+ "SIMD lane width [" + species.length() + "]");
+ }
+ }
+
+ /**
+ * Checks the I/Q sample array length to be an integer multiple of the SIMD lane width.
+ * @param array samples
+ * @param species used for SIMD operations
+ */
+ public static void checkArrayLength(float[] array, VectorSpecies species)
+ {
+ if(array.length % species.length() != 0)
+ {
+ throw new IllegalArgumentException("Buffer array length [" + array.length + "] must be a power of 2 multiple " +
+ "of SIMD lane width [" + species.length() + "]");
}
}
diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationManager.java b/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationManager.java
index e40faaf8..0dac3acb 100644
--- a/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationManager.java
+++ b/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationManager.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* 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
@@ -19,13 +19,13 @@
package io.github.dsheirer.vector.calibrate;
+import io.github.dsheirer.dsp.am.AmDemodulatorCalibration;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.calibration.VectorCalibrationPreference;
import io.github.dsheirer.vector.calibrate.airspy.AirspySampleConverterCalibration;
import io.github.dsheirer.vector.calibrate.airspy.AirspyUnpackedCalibration;
import io.github.dsheirer.vector.calibrate.airspy.AirspyUnpackedInterleavedCalibration;
import io.github.dsheirer.vector.calibrate.demodulator.FmDemodulatorCalibration;
-import io.github.dsheirer.vector.calibrate.demodulator.SquelchingFmDemodulatorCalibration;
import io.github.dsheirer.vector.calibrate.filter.FirFilterCalibration;
import io.github.dsheirer.vector.calibrate.filter.RealDcRemovalCalibration;
import io.github.dsheirer.vector.calibrate.filter.RealHalfBand11TapFilterCalibration;
@@ -35,19 +35,19 @@ import io.github.dsheirer.vector.calibrate.filter.RealHalfBand63TapFilterCalibra
import io.github.dsheirer.vector.calibrate.filter.RealHalfBandDefaultFilterCalibration;
import io.github.dsheirer.vector.calibrate.gain.ComplexGainCalibration;
import io.github.dsheirer.vector.calibrate.gain.ComplexGainControlCalibration;
+import io.github.dsheirer.vector.calibrate.magnitude.MagnitudeCalibration;
import io.github.dsheirer.vector.calibrate.mixer.ComplexMixerCalibration;
import io.github.dsheirer.vector.calibrate.oscillator.ComplexOscillatorCalibration;
import io.github.dsheirer.vector.calibrate.oscillator.RealOscillatorCalibration;
import io.github.dsheirer.vector.calibrate.window.WindowCalibration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Determines the optimal (scalar vs vector) class to use for the current CPU architecture.
@@ -99,12 +99,14 @@ public class CalibrationManager
sInstance.add(new AirspySampleConverterCalibration());
sInstance.add(new AirspyUnpackedCalibration());
sInstance.add(new AirspyUnpackedInterleavedCalibration());
+ sInstance.add(new AmDemodulatorCalibration());
sInstance.add(new ComplexGainCalibration());
sInstance.add(new ComplexGainControlCalibration());
sInstance.add(new ComplexOscillatorCalibration());
sInstance.add(new ComplexMixerCalibration());
sInstance.add(new FirFilterCalibration());
sInstance.add(new FmDemodulatorCalibration());
+ sInstance.add(new MagnitudeCalibration());
sInstance.add(new RealDcRemovalCalibration());
sInstance.add(new RealHalfBand11TapFilterCalibration());
sInstance.add(new RealHalfBand15TapFilterCalibration());
@@ -112,7 +114,6 @@ public class CalibrationManager
sInstance.add(new RealHalfBand63TapFilterCalibration());
sInstance.add(new RealHalfBandDefaultFilterCalibration());
sInstance.add(new RealOscillatorCalibration());
- sInstance.add(new SquelchingFmDemodulatorCalibration());
// sInstance.add(new HilbertCalibration()); //Not currently used
sInstance.add(new WindowCalibration()); //Not currently used
diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationType.java b/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationType.java
index 3a0d77c7..edab2d4d 100644
--- a/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationType.java
+++ b/src/main/java/io/github/dsheirer/vector/calibrate/CalibrationType.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* 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
@@ -35,6 +35,7 @@ public enum CalibrationType
AIRSPY_SAMPLE_CONVERTER("Airspy Sample Converter", 1),
AIRSPY_UNPACKED_INTERLEAVED_ITERATOR("Airspy Unpacked Interleaved Iterator", 1),
AIRSPY_UNPACKED_ITERATOR("Airspy Unpacked Iterator", 1),
+ AM_DEMODULATOR("AM Demodulator", 1),
DC_REMOVAL_REAL("Real DC Removal Filter", 1),
FILTER_FIR("FIR Filter", 1),
FILTER_HALF_BAND_REAL_11_TAP("Real Half-Band Decimation Filter - 11 Tap", 1),
@@ -42,9 +43,10 @@ public enum CalibrationType
FILTER_HALF_BAND_REAL_23_TAP("Real Half-Band Decimation Filter - 23 Tap", 1),
FILTER_HALF_BAND_REAL_63_TAP("Real Half-Band Decimation Filter - 63 Tap", 1),
FILTER_HALF_BAND_REAL_DEFAULT("Real Half-Band Decimation Filter - Default", 1),
- FM_DEMODULATOR("FM Demodulator", 1),
+ FM_DEMODULATOR("FM Demodulator", 2),
GAIN_CONTROL_COMPLEX("Complex Gain Control", 1),
HILBERT_TRANSFORM("Hilbert Transform", 1),
+ MAGNITUDE("Magnitude", 1),
MIXER_COMPLEX("Complex Mixer", 1),
OSCILLATOR_REAL("Real Oscillator", 1),
SQUELCHING_FM_DEMODULATOR("Squelching FM Demodulator", 1),
diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/demodulator/FmDemodulatorCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/demodulator/FmDemodulatorCalibration.java
index 20ae919f..52f0d851 100644
--- a/src/main/java/io/github/dsheirer/vector/calibrate/demodulator/FmDemodulatorCalibration.java
+++ b/src/main/java/io/github/dsheirer/vector/calibrate/demodulator/FmDemodulatorCalibration.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 Dennis Sheirer
*
* 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
@@ -19,9 +19,12 @@
package io.github.dsheirer.vector.calibrate.demodulator;
-import io.github.dsheirer.dsp.fm.IFmDemodulator;
+import io.github.dsheirer.dsp.fm.IDemodulator;
import io.github.dsheirer.dsp.fm.ScalarFMDemodulator;
-import io.github.dsheirer.dsp.fm.VectorFMDemodulator;
+import io.github.dsheirer.dsp.fm.VectorFMDemodulator128;
+import io.github.dsheirer.dsp.fm.VectorFMDemodulator256;
+import io.github.dsheirer.dsp.fm.VectorFMDemodulator512;
+import io.github.dsheirer.dsp.fm.VectorFMDemodulator64;
import io.github.dsheirer.vector.calibrate.Calibration;
import io.github.dsheirer.vector.calibrate.CalibrationException;
import io.github.dsheirer.vector.calibrate.CalibrationType;
@@ -37,9 +40,11 @@ public class FmDemodulatorCalibration extends Calibration
private static final int ITERATION_DURATION_MS = 1000;
private static final int WARMUP_ITERATIONS = 5;
private static final int TEST_ITERATIONS = 5;
- private IFmDemodulator mScalarDemodulator = new ScalarFMDemodulator();
- private IFmDemodulator mVectorDemodulator = new VectorFMDemodulator();
-
+ private IDemodulator mScalarDemodulator = new ScalarFMDemodulator();
+ private IDemodulator mVectorDemodulator64 = new VectorFMDemodulator64();
+ private IDemodulator mVectorDemodulator128 = new VectorFMDemodulator128();
+ private IDemodulator mVectorDemodulator256 = new VectorFMDemodulator256();
+ private IDemodulator mVectorDemodulator512 = new VectorFMDemodulator512();
/**
* Constructs an instance
@@ -64,15 +69,45 @@ public class FmDemodulatorCalibration extends Calibration
mLog.info("FM DEMODULATOR WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
- Mean vectorMean = new Mean();
+ Mean vectorMean64 = new Mean();
for(int x = 0; x < WARMUP_ITERATIONS; x++)
{
- long score = testVector(i, q);
- vectorMean.increment(score);
+ long score = testVector64(i, q);
+ vectorMean64.increment(score);
}
- mLog.info("FM DEMODULATOR WARMUP - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
+ mLog.info("FM DEMODULATOR WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean64.getResult()));
+
+ Mean vectorMean128 = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testVector128(i, q);
+ vectorMean128.increment(score);
+ }
+
+ mLog.info("FM DEMODULATOR WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean128.getResult()));
+
+ Mean vectorMean256 = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testVector256(i, q);
+ vectorMean256.increment(score);
+ }
+
+ mLog.info("FM DEMODULATOR WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean256.getResult()));
+
+ Mean vectorMean512 = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testVector512(i, q);
+ vectorMean512.increment(score);
+ }
+
+ mLog.info("FM DEMODULATOR WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean512.getResult()));
//Start tests
scalarMean.clear();
@@ -85,25 +120,74 @@ public class FmDemodulatorCalibration extends Calibration
mLog.info("FM DEMODULATOR - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
- vectorMean.clear();
+ vectorMean64.clear();
for(int x = 0; x < TEST_ITERATIONS; x++)
{
- long score = testVector(i, q);
- vectorMean.increment(score);
+ long score = testVector64(i, q);
+ vectorMean64.increment(score);
}
- mLog.info("FM DEMODULATOR - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
+ mLog.info("FM DEMODULATOR - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean64.getResult()));
- if(scalarMean.getResult() > vectorMean.getResult())
+ vectorMean128.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
{
- setImplementation(Implementation.SCALAR);
+ long score = testVector128(i, q);
+ vectorMean128.increment(score);
}
- else
+
+ mLog.info("FM DEMODULATOR - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean128.getResult()));
+
+ vectorMean256.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
{
- setImplementation(Implementation.VECTOR_SIMD_PREFERRED);
+ long score = testVector256(i, q);
+ vectorMean256.increment(score);
}
+ mLog.info("FM DEMODULATOR - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean256.getResult()));
+
+ vectorMean512.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
+ {
+ long score = testVector512(i, q);
+ vectorMean512.increment(score);
+ }
+
+ mLog.info("FM DEMODULATOR - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean512.getResult()));
+
+ Implementation best = Implementation.SCALAR;
+ double bestScore = scalarMean.getResult();
+
+ if(vectorMean64.getResult() > bestScore)
+ {
+ bestScore = vectorMean64.getResult();
+ best = Implementation.VECTOR_SIMD_64;
+ }
+
+ if(vectorMean128.getResult() > bestScore)
+ {
+ bestScore = vectorMean128.getResult();
+ best = Implementation.VECTOR_SIMD_128;
+ }
+
+ if(vectorMean256.getResult() > bestScore)
+ {
+ bestScore = vectorMean256.getResult();
+ best = Implementation.VECTOR_SIMD_256;
+ }
+
+ if(vectorMean512.getResult() > bestScore)
+ {
+ best = Implementation.VECTOR_SIMD_512;
+ }
+
+ setImplementation(best);
+
mLog.info("FM DEMODULATOR - SET OPTIMAL IMPLEMENTATION TO:" + getImplementation());
}
@@ -124,7 +208,7 @@ public class FmDemodulatorCalibration extends Calibration
return count + (long)(accumulator * 0);
}
- private long testVector(float[] i, float[] q)
+ private long testVector64(float[] i, float[] q)
{
double accumulator = 0.0;
long count = 0;
@@ -132,11 +216,73 @@ public class FmDemodulatorCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
- float[] demodulated = mVectorDemodulator.demodulate(i, q);
+ float[] demodulated = mVectorDemodulator64.demodulate(i, q);
accumulator += demodulated[1];
count++;
}
return count + (long)(accumulator * 0);
}
+
+ private long testVector128(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mVectorDemodulator128.demodulate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ private long testVector256(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mVectorDemodulator256.demodulate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ private long testVector512(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mVectorDemodulator512.demodulate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ public static void main(String[] args)
+ {
+ FmDemodulatorCalibration calibration = new FmDemodulatorCalibration();
+
+ try
+ {
+ calibration.calibrate();
+ }
+ catch(Exception e)
+ {
+ mLog.error("Error during calibration", e);
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/demodulator/SquelchingFmDemodulatorCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/demodulator/SquelchingFmDemodulatorCalibration.java
deleted file mode 100644
index 1380a571..00000000
--- a/src/main/java/io/github/dsheirer/vector/calibrate/demodulator/SquelchingFmDemodulatorCalibration.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
- *
- * 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 3 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, see
- * ****************************************************************************
- */
-
-package io.github.dsheirer.vector.calibrate.demodulator;
-
-import io.github.dsheirer.dsp.fm.IFmDemodulator;
-import io.github.dsheirer.dsp.fm.ScalarSquelchingFMDemodulator;
-import io.github.dsheirer.dsp.fm.VectorSquelchingFMDemodulator;
-import io.github.dsheirer.vector.calibrate.Calibration;
-import io.github.dsheirer.vector.calibrate.CalibrationException;
-import io.github.dsheirer.vector.calibrate.CalibrationType;
-import io.github.dsheirer.vector.calibrate.Implementation;
-import org.apache.commons.math3.stat.descriptive.moment.Mean;
-
-/**
- * Calibrates squelching FM demodulator options
- */
-public class SquelchingFmDemodulatorCalibration extends Calibration
-{
- private static final float POWER_SQUELCH_ALPHA_DECAY = 0.0004f;
- private static final float POWER_SQUELCH_THRESHOLD_DB = -78.0f;
- private static final int POWER_SQUELCH_RAMP = 4;
- private static final int BUFFER_SIZE = 2048;
- private static final int ITERATION_DURATION_MS = 1000;
- private static final int WARMUP_ITERATIONS = 5;
- private static final int TEST_ITERATIONS = 5;
-
- private IFmDemodulator mScalar = new ScalarSquelchingFMDemodulator(POWER_SQUELCH_ALPHA_DECAY,
- POWER_SQUELCH_THRESHOLD_DB, POWER_SQUELCH_RAMP);
- private IFmDemodulator mVector = new VectorSquelchingFMDemodulator(POWER_SQUELCH_ALPHA_DECAY,
- POWER_SQUELCH_THRESHOLD_DB, POWER_SQUELCH_RAMP);
-
- /**
- * Constructs an instance
- */
- public SquelchingFmDemodulatorCalibration()
- {
- super(CalibrationType.SQUELCHING_FM_DEMODULATOR);
- }
-
- @Override public void calibrate() throws CalibrationException
- {
- float[] i = getFloatSamples(BUFFER_SIZE);
- float[] q = getFloatSamples(BUFFER_SIZE);
-
- Mean scalarMean = new Mean();
-
- for(int x = 0; x < WARMUP_ITERATIONS; x++)
- {
- long score = testScalar(i, q);
- scalarMean.increment(score);
- }
-
- mLog.info("SQUELCHING FM DEMODULATOR WARMUP - SCALAR:" + DECIMAL_FORMAT.format(scalarMean.getResult()));
-
- Mean vectorMean = new Mean();
-
- for(int x = 0; x < WARMUP_ITERATIONS; x++)
- {
- long score = testVector(i, q);
- vectorMean.increment(score);
- }
-
- mLog.info("SQUELCHING FM DEMODULATOR WARMUP - VECTOR:" + DECIMAL_FORMAT.format(vectorMean.getResult()));
-
- //Test begins ...
- scalarMean.clear();
-
- for(int x = 0; x < TEST_ITERATIONS; x++)
- {
- long score = testScalar(i, q);
- scalarMean.increment(score);
- }
-
- mLog.info("SQUELCHING FM DEMODULATOR - SCALAR:" + DECIMAL_FORMAT.format(scalarMean.getResult()));
-
- vectorMean.clear();
-
- for(int x = 0; x < TEST_ITERATIONS; x++)
- {
- long score = testVector(i, q);
- vectorMean.increment(score);
- }
-
- mLog.info("SQUELCHING FM DEMODULATOR - VECTOR:" + DECIMAL_FORMAT.format(vectorMean.getResult()));
-
- if(scalarMean.getResult() > vectorMean.getResult())
- {
- setImplementation(Implementation.SCALAR);
- }
- else
- {
- setImplementation(Implementation.VECTOR_SIMD_PREFERRED);
- }
-
- mLog.info("SQUELCHING FM DEMODULATOR - SET OPTIMAL IMPLEMENTATION TO:" + getImplementation());
- }
-
- private long testScalar(float[] i, float[] q)
- {
- double accumulator = 0.0;
- long count = 0;
-
- long start = System.currentTimeMillis();
-
- while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
- {
- float[] demodulated = mScalar.demodulate(i, q);
- accumulator += demodulated[1];
- count++;
- }
-
- return count + (long)(accumulator * 0);
- }
-
- private long testVector(float[] i, float[] q)
- {
- double accumulator = 0.0;
- long count = 0;
-
- long start = System.currentTimeMillis();
-
- while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
- {
- float[] demodulated = mVector.demodulate(i, q);
- accumulator += demodulated[1];
- count++;
- }
-
- return count + (long)(accumulator * 0);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/vector/calibrate/magnitude/MagnitudeCalibration.java b/src/main/java/io/github/dsheirer/vector/calibrate/magnitude/MagnitudeCalibration.java
new file mode 100644
index 00000000..e30e6d86
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/vector/calibrate/magnitude/MagnitudeCalibration.java
@@ -0,0 +1,288 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 Dennis Sheirer
+ *
+ * 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 3 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, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.vector.calibrate.magnitude;
+
+import io.github.dsheirer.dsp.magnitude.IMagnitudeCalculator;
+import io.github.dsheirer.dsp.magnitude.ScalarMagnitudeCalculator;
+import io.github.dsheirer.dsp.magnitude.VectorMagnitudeCalculator128;
+import io.github.dsheirer.dsp.magnitude.VectorMagnitudeCalculator256;
+import io.github.dsheirer.dsp.magnitude.VectorMagnitudeCalculator512;
+import io.github.dsheirer.dsp.magnitude.VectorMagnitudeCalculator64;
+import io.github.dsheirer.vector.calibrate.Calibration;
+import io.github.dsheirer.vector.calibrate.CalibrationException;
+import io.github.dsheirer.vector.calibrate.CalibrationType;
+import io.github.dsheirer.vector.calibrate.Implementation;
+import org.apache.commons.math3.stat.descriptive.moment.Mean;
+
+/**
+ * Calibrates FM demodulator options
+ */
+public class MagnitudeCalibration extends Calibration
+{
+ private static final int BUFFER_SIZE = 2048;
+ private static final int ITERATION_DURATION_MS = 1000;
+ private static final int WARMUP_ITERATIONS = 5;
+ private static final int TEST_ITERATIONS = 5;
+ private final IMagnitudeCalculator mScalarMagnitude = new ScalarMagnitudeCalculator();
+ private final IMagnitudeCalculator mVectorMagnitude64 = new VectorMagnitudeCalculator64();
+ private final IMagnitudeCalculator mVectorMagnitude128 = new VectorMagnitudeCalculator128();
+ private final IMagnitudeCalculator mVectorMagnitude256 = new VectorMagnitudeCalculator256();
+ private final IMagnitudeCalculator mVectorMagnitude512 = new VectorMagnitudeCalculator512();
+
+ /**
+ * Constructs an instance
+ */
+ public MagnitudeCalibration()
+ {
+ super(CalibrationType.MAGNITUDE);
+ }
+
+ @Override public void calibrate() throws CalibrationException
+ {
+ float[] i = getFloatSamples(BUFFER_SIZE);
+ float[] q = getFloatSamples(BUFFER_SIZE);
+
+ Mean scalarMean = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testScalar(i, q);
+ scalarMean.increment(score);
+ }
+
+ mLog.info("MAGNITUDE WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
+
+ Mean vectorMean64 = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testVector64(i, q);
+ vectorMean64.increment(score);
+ }
+
+ mLog.info("MAGNITUDE WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean64.getResult()));
+
+ Mean vectorMean128 = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testVector128(i, q);
+ vectorMean128.increment(score);
+ }
+
+ mLog.info("MAGNITUDE WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean128.getResult()));
+
+ Mean vectorMean256 = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testVector256(i, q);
+ vectorMean256.increment(score);
+ }
+
+ mLog.info("MAGNITUDE WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean256.getResult()));
+
+ Mean vectorMean512 = new Mean();
+
+ for(int x = 0; x < WARMUP_ITERATIONS; x++)
+ {
+ long score = testVector512(i, q);
+ vectorMean512.increment(score);
+ }
+
+ mLog.info("MAGNITUDE WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean512.getResult()));
+
+ //Start tests
+ scalarMean.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
+ {
+ long score = testScalar(i, q);
+ scalarMean.increment(score);
+ }
+
+ mLog.info("MAGNITUDE - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
+
+ vectorMean64.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
+ {
+ long score = testVector64(i, q);
+ vectorMean64.increment(score);
+ }
+
+ mLog.info("MAGNITUDE - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean64.getResult()));
+
+ vectorMean128.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
+ {
+ long score = testVector128(i, q);
+ vectorMean128.increment(score);
+ }
+
+ mLog.info("MAGNITUDE - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean128.getResult()));
+
+ vectorMean256.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
+ {
+ long score = testVector256(i, q);
+ vectorMean256.increment(score);
+ }
+
+ mLog.info("MAGNITUDE - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean256.getResult()));
+
+ vectorMean512.clear();
+
+ for(int x = 0; x < TEST_ITERATIONS; x++)
+ {
+ long score = testVector512(i, q);
+ vectorMean512.increment(score);
+ }
+
+ mLog.info("MAGNITUDE - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean512.getResult()));
+
+ Implementation best = Implementation.SCALAR;
+ double bestScore = scalarMean.getResult();
+
+ if(vectorMean64.getResult() > bestScore)
+ {
+ bestScore = vectorMean64.getResult();
+ best = Implementation.VECTOR_SIMD_64;
+ }
+
+ if(vectorMean128.getResult() > bestScore)
+ {
+ bestScore = vectorMean128.getResult();
+ best = Implementation.VECTOR_SIMD_128;
+ }
+
+ if(vectorMean256.getResult() > bestScore)
+ {
+ bestScore = vectorMean256.getResult();
+ best = Implementation.VECTOR_SIMD_256;
+ }
+
+ if(vectorMean512.getResult() > bestScore)
+ {
+ best = Implementation.VECTOR_SIMD_512;
+ }
+
+ setImplementation(best);
+
+ mLog.info("MAGNITUDE - SET OPTIMAL IMPLEMENTATION TO:" + getImplementation());
+ }
+
+ private long testScalar(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mScalarMagnitude.calculate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ private long testVector64(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mVectorMagnitude64.calculate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ private long testVector128(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mVectorMagnitude128.calculate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ private long testVector256(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mVectorMagnitude256.calculate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ private long testVector512(float[] i, float[] q)
+ {
+ double accumulator = 0.0;
+ long count = 0;
+ long start = System.currentTimeMillis();
+
+ while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
+ {
+ float[] demodulated = mVectorMagnitude512.calculate(i, q);
+ accumulator += demodulated[1];
+ count++;
+ }
+
+ return count + (long)(accumulator * 0);
+ }
+
+ public static void main(String[] args)
+ {
+ MagnitudeCalibration calibration = new MagnitudeCalibration();
+
+ try
+ {
+ calibration.calibrate();
+ }
+ catch(Exception e)
+ {
+ mLog.error("Error during calibration", e);
+ }
+ }
+}