Resolves #373 - Adds support for Tuner frequency error PPM monitoring

and auto-correction for R820T tuners.

-Updated TunerModel and TunerViewPanel to add 2 new columns to
display the current tuner's PPM setting and (when available) the
measured frequency error averaged over 5 second intervals provided by
certain decoders (e.g. P25 C4FM and LSM).  Frequency error value from
P25 decoders is provided from the Costas Loop PLL once 2 or more P25
message sync events have been observed in a short period.
-Updated R820T tuner to auto-correct PPM when measured frequency error
exceeds the +/- 0.4PPM threshold for a period of 30 seconds or more.
Feature can be enabled/disabled by user in the tuner editor panel.

Resolved several issues with P25 C4FM/LSM decoders that was impacting
performance related to channel misalignment from tuner PPM offsets.
This commit is contained in:
Denny
2018-07-29 10:48:29 -04:00
parent 5c0d97d942
commit 0c47efea03
44 changed files with 1296 additions and 483 deletions

View File

@@ -35,17 +35,16 @@ import io.github.dsheirer.module.decode.event.ICallEventProvider;
import io.github.dsheirer.sample.IOverflowListener;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.ISourceEventListener;
import io.github.dsheirer.source.ISourceEventProvider;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.heartbeat.Heartbeat;
import io.github.dsheirer.source.heartbeat.IHeartbeatListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static io.github.dsheirer.source.SourceEvent.Event.NOTIFICATION_FREQUENCY_CHANGE;
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
IDecoderStateEventProvider, ISourceEventListener, IHeartbeatListener, ISquelchStateProvider,
IAttributeChangeRequestListener, IOverflowListener
IDecoderStateEventProvider, ISourceEventListener, ISourceEventProvider, IHeartbeatListener,
ISquelchStateProvider, IAttributeChangeRequestListener, IOverflowListener
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
@@ -57,12 +56,13 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
private Listener<CallEvent> mCallEventListener;
private Listener<DecoderStateEvent> mDecoderStateListener;
private Listener<SquelchState> mSquelchStateListener;
private Listener<SourceEvent> mExternalSourceEventListener;
private DecoderStateEventReceiver mDecoderStateEventReceiver = new DecoderStateEventReceiver();
private HeartbeatReceiver mHeartbeatReceiver = new HeartbeatReceiver();
private ChannelType mChannelType;
private TrafficChannelManager mTrafficChannelEndListener;
private CallEvent mTrafficChannelCallEvent;
private SourceChangeListener mSourceChangeListener;
private SourceEventListener mInternalSourceEventListener;
private boolean mSquelchLocked = false;
private boolean mSelected = false;
@@ -160,12 +160,12 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
@Override
public Listener<SourceEvent> getSourceEventListener()
{
if(mSourceChangeListener == null)
if(mInternalSourceEventListener == null)
{
mSourceChangeListener = new SourceChangeListener();
mInternalSourceEventListener = new SourceEventListener();
}
return mSourceChangeListener;
return mInternalSourceEventListener;
}
private boolean isStandardChannel()
@@ -255,6 +255,17 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
}
}
/**
* Broadcasts the source event to a registered external source event listener
*/
protected void broadcast(SourceEvent sourceEvent)
{
if(mExternalSourceEventListener != null)
{
mExternalSourceEventListener.receive(sourceEvent);
}
}
/**
* Sets the squelch state listener
*/
@@ -516,18 +527,46 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
}
/**
* Listener to receive frequency change events and rebroadcast them to the decoder states
* Registers the listener to receive source events from the channel state
*/
public class SourceChangeListener implements Listener<SourceEvent>
@Override
public void setSourceEventListener(Listener<SourceEvent> listener)
{
mExternalSourceEventListener = listener;
}
/**
* De-Registers a listener from receiving source events from the channel state
*/
@Override
public void removeSourceEventListener()
{
mExternalSourceEventListener = null;
}
/**
* Listener to receive source events.
*/
public class SourceEventListener implements Listener<SourceEvent>
{
@Override
public void receive(SourceEvent sourceEvent)
{
if(sourceEvent.getEvent() == NOTIFICATION_FREQUENCY_CHANGE)
switch(sourceEvent.getEvent())
{
long frequency = sourceEvent.getValue().longValue();
broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
case NOTIFICATION_FREQUENCY_CHANGE:
//Rebroadcast source frequency change events for the decoder(s) to process
long frequency = sourceEvent.getValue().longValue();
broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
break;
case NOTIFICATION_MEASURED_FREQUENCY_ERROR:
//Rebroadcast frequency error measurements to external listener if we're currently
//in an active (ie sync locked) state.
if(getState().isActiveState())
{
broadcast(SourceEvent.frequencyErrorMeasurementSyncLocked(sourceEvent.getValue().longValue(), mChannelType.name()));
}
break;
}
}
}