#35 Updates AM & NBFM decoders to use Adaptive Squelch, a power squelch that includes an adaptive noise floor monitor. Implements vectorized magnitude operations for AM & FM decoders. Adds talkgroup assignment and squelch controls to AM channel configuration editor. Implements new AM gain control that attempts to normalize audio gain quickly at beginning of call/transmission and maintains that gain across lifecycle of the call and resets the gain for the next call. (#1540)

Notes on usage:
- When using either AM or FM squelch control use either the Heterodyne channelizer or if using the Polyphase channelizer, avoid using trunk tracking decoders like DMR or P25 or set the squelch threshold high enough to avoid changes in noise floor from polyphase channelizer where it can vary between using the one channel or the two-channel combined channel sources.

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2023-05-07 07:27:44 -04:00
committed by GitHub
parent 5654254cbb
commit 4f8b8e978b
74 changed files with 4630 additions and 2012 deletions

View File

@@ -1,52 +1,75 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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 <http://www.gnu.org/licenses/>
******************************************************************************/
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.am;
import io.github.dsheirer.dsp.am.SquelchingAMDemodulator;
import io.github.dsheirer.dsp.gain.AudioGainAndDcFilter;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.PrimaryDecoder;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.analog.SquelchingAnalogDecoder;
public class AMDecoder extends PrimaryDecoder
/**
* Analog AM radio decoder module with integrated squelching control
*/
public class AMDecoder extends SquelchingAnalogDecoder
{
public AMDecoder( DecodeConfiguration config )
{
super( config );
}
private static final float DEMODULATOR_GAIN = 250.0f;
private static final float SQUELCH_ALPHA_DECAY = 0.0004f;
private static final float MINIMUM_GAIN = 0.5f;
private static final float MAXIMUM_GAIN = 16.0f;
private static final float OBJECTIVE_AUDIO_AMPLITUDE = 0.75f;
private AudioGainAndDcFilter mAGC = new AudioGainAndDcFilter(MINIMUM_GAIN, MAXIMUM_GAIN, OBJECTIVE_AUDIO_AMPLITUDE);
@Override
public DecoderType getDecoderType()
/**
* Constructs an instance
* @param config for the AM channel.
*/
public AMDecoder(DecodeConfigAM config)
{
return DecoderType.AM;
super(config, new SquelchingAMDemodulator(DEMODULATOR_GAIN, SQUELCH_ALPHA_DECAY,
config.getSquelchThreshold(), config.isSquelchAutoTrack()));
}
@Override
public void reset()
{
}
@Override
public void start()
public DecoderType getDecoderType()
{
return DecoderType.AM;
}
@Override
public void stop()
{
}
/**
* Overrides the broadcast of the final demodulated audio samples so that we can apply AGC
* @param demodulatedSamples to broadcast
*/
@Override
protected void broadcast(float[] demodulatedSamples)
{
//Apply audio gain and rebroadcast
super.broadcast(mAGC.process(demodulatedSamples));
}
/**
* Overrides the notify call start so that we can reset the AGC
*/
@Override
protected void notifyCallStart()
{
mAGC.reset();
super.notifyCallStart();
}
}

View File

@@ -0,0 +1,79 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.am;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierClass;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.identifier.string.SimpleStringIdentifier;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.analog.AnalogDecoderState;
/**
* AM decoder state
*/
public class AMDecoderState extends AnalogDecoderState
{
private String mChannelName;
private Identifier mChannelNameIdentifier;
private Identifier mTalkgroupIdentifier;
/**
* Constructs an instance
* @param channelName to use for this channel
* @param decodeConfig with talkgroup identifier
*/
public AMDecoderState(String channelName, DecodeConfigAM decodeConfig)
{
mChannelName = (channelName != null && !channelName.isEmpty()) ? channelName : "AM CHANNEL";
mChannelNameIdentifier = new SimpleStringIdentifier(mChannelName, IdentifierClass.CONFIGURATION, Form.CHANNEL_NAME, Role.ANY);
mTalkgroupIdentifier = new AMTalkgroup(decodeConfig.getTalkgroup());
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.AM;
}
@Override
protected Identifier getChannelNameIdentifier()
{
return mChannelNameIdentifier;
}
@Override
protected Identifier getTalkgroupIdentifier()
{
return mTalkgroupIdentifier;
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
sb.append("Activity Summary\n");
sb.append("\tDecoder: AM");
sb.append("\n\n");
return sb.toString();
}
}

View File

@@ -0,0 +1,46 @@
/*
* *****************************************************************************
* 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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.am;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.protocol.Protocol;
/**
* AM talkgroup. Note: this is a logical identifier that is assignable from a user-specified
* configuration so that AM audio events are compatible with other features of the sdrtrunk system.
*/
public class AMTalkgroup extends TalkgroupIdentifier
{
/**
* Constructs an instance
* @param value 1-65,535
*/
public AMTalkgroup(int value)
{
super(value, Role.TO);
}
@Override
public Protocol getProtocol()
{
return Protocol.AM;
}
}

View File

@@ -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,33 +14,35 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* *****************************************************************************
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.am;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.analog.DecodeConfigAnalog;
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
public class DecodeConfigAM extends DecodeConfiguration
/**
* AM decoder configuration
*/
public class DecodeConfigAM extends DecodeConfigAnalog
{
private boolean mRecordAudio = false;
public DecodeConfigAM()
{
}
@JacksonXmlProperty(isAttribute = true, localName = "recordAudio")
public boolean getRecordAudio()
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
public DecoderType getDecoderType()
{
return mRecordAudio;
return DecoderType.AM;
}
public void setRecordAudio(boolean recordAudio)
@Override
protected Bandwidth getDefaultBandwidth()
{
mRecordAudio = recordAudio;
return Bandwidth.BW_15_0;
}
/**
@@ -51,13 +52,20 @@ public class DecodeConfigAM extends DecodeConfiguration
@Override
public ChannelSpecification getChannelSpecification()
{
return new ChannelSpecification(25000.0,
3000, 3000.0, 5000.0);
}
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
public DecoderType getDecoderType()
{
return DecoderType.AM;
switch(getBandwidth())
{
case BW_3_0:
return new ChannelSpecification(25000.0, 3000, 1500.0, 1700.0);
case BW_5_0:
return new ChannelSpecification(25000.0, 5000, 2500.0, 2700.0);
case BW_8_33:
return new ChannelSpecification(25000.0, 10000, 5000.0, 7000.0);
case BW_15_0:
return new ChannelSpecification(25000.0, 15000, 7500.0, 9500.0);
case BW_25_0:
return new ChannelSpecification(25000.0, 25000, 12500.0, 14500.0);
default:
throw new IllegalArgumentException("Unrecognized AM bandwidth value: " + getBandwidth());
}
}
}