mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
P25 and Airspy work in progress
Updated p25 decoder state to better track calls and call detects from a trunked channel Fixed issue with false detects on LDU2 encrypted call messages Fixed Airspy channel allocation ... will allocate channels across the entire bandwidth, minus 50 kHz on either end, and will not adjust the center frequency if it's not needed. Fixed traffic channel teardown -- traffic channels are immediately removed now, without a fade delay. AudioMetadata - updated flag is now reset upon copying the metadata
This commit is contained in:
@@ -124,9 +124,6 @@ public class AudioModule extends Module implements IAudioPacketProvider,
|
||||
|
||||
AudioPacket packet = new AudioPacket( audio, mAudioMetadata.copyOf() );
|
||||
|
||||
/* Reset the updated flag on the metadata */
|
||||
mAudioMetadata.setUpdated( false );
|
||||
|
||||
mAudioPacketListener.receive( packet );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,9 @@ public class AudioMetadata implements Listener<Metadata>
|
||||
copy.mRecordable = mRecordable;
|
||||
copy.mMetadata.addAll( mMetadata );
|
||||
copy.mUpdated = mUpdated;
|
||||
copy.mIdentifier = mIdentifier;
|
||||
copy.mIdentifier = new String( mIdentifier );
|
||||
|
||||
mUpdated = false;
|
||||
|
||||
return copy;
|
||||
}
|
||||
@@ -283,4 +285,25 @@ public class AudioMetadata implements Listener<Metadata>
|
||||
mRecordable = recordable;
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Audio Metadata - source:" );
|
||||
sb.append( getSource() );
|
||||
sb.append( " priority:" );
|
||||
sb.append( getPriority() );
|
||||
sb.append( " updated:" + isUpdated() );
|
||||
sb.append( " " );
|
||||
for( Metadata m: getMetadata() )
|
||||
{
|
||||
sb.append( m.getKey() );
|
||||
sb.append( ":" );
|
||||
sb.append( m.getValue() );
|
||||
sb.append( " " );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public class P25DecoderPanel extends DecoderPanel
|
||||
}
|
||||
break;
|
||||
case TO_TALKGROUP_ALIAS:
|
||||
Alias to = state.getToAlias();
|
||||
final Alias to = state.getToAlias();
|
||||
|
||||
if( to != null )
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -177,6 +178,7 @@ public class P25DecoderState extends DecoderState
|
||||
|
||||
private P25CallEvent mCurrentCallEvent;
|
||||
private List<String> mCallDetectTalkgroups = new ArrayList<>();
|
||||
private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>();
|
||||
|
||||
public P25DecoderState( AliasList aliasList,
|
||||
ChannelType channelType,
|
||||
@@ -256,6 +258,90 @@ public class P25DecoderState extends DecoderState
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there a call event exists for the specified channel and
|
||||
* from and to talkgroup identifiers.
|
||||
*
|
||||
* @param channel - channel number
|
||||
* @param from user id
|
||||
* @param to talkgroup or user id
|
||||
* @param time current in millis
|
||||
* @return true if there is a call event
|
||||
*/
|
||||
public boolean hasCallEvent( String channel, String from, String to )
|
||||
{
|
||||
boolean hasEvent = false;
|
||||
|
||||
if( mChannelCallMap.containsKey( channel ) )
|
||||
{
|
||||
P25CallEvent event = mChannelCallMap.get( channel );
|
||||
|
||||
if( to != null &&
|
||||
event.getToID() != null &&
|
||||
to.contentEquals( event.getToID() ) )
|
||||
{
|
||||
if( from != null )
|
||||
{
|
||||
if( event.getFromID() == null )
|
||||
{
|
||||
hasEvent = true;
|
||||
}
|
||||
else if( from.contentEquals( event.getFromID() ) )
|
||||
{
|
||||
hasEvent = true;
|
||||
}
|
||||
}
|
||||
else if( event.getFromID() == null )
|
||||
{
|
||||
hasEvent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the channel and event to the current channel call map. If an entry
|
||||
* already exists, terminates the event and broadcasts the update.
|
||||
*
|
||||
* @param channel for the call
|
||||
* @param event to place in the map
|
||||
*/
|
||||
public void registerCallEvent( P25CallEvent event )
|
||||
{
|
||||
if( mChannelCallMap.containsKey( event.getChannel() ) )
|
||||
{
|
||||
P25CallEvent previousEvent = mChannelCallMap.remove( event.getChannel() );
|
||||
|
||||
previousEvent.end();
|
||||
|
||||
broadcast( previousEvent );
|
||||
}
|
||||
|
||||
mChannelCallMap.put( event.getChannel(), event );
|
||||
}
|
||||
|
||||
public void updateCallEvent( String channel, String from, String to )
|
||||
{
|
||||
P25CallEvent event = mChannelCallMap.get( channel );
|
||||
|
||||
if( event != null &&
|
||||
to != null &&
|
||||
event.getToID() != null &&
|
||||
event.getToID().contentEquals( to ) )
|
||||
{
|
||||
if( event.getFromID() == null &&
|
||||
from != null &&
|
||||
!from.contentEquals( "0000" ) &&
|
||||
!from.contentEquals( "000000" ) )
|
||||
{
|
||||
event.setFromID( from );
|
||||
broadcast( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary message processing method.
|
||||
*/
|
||||
@@ -1937,12 +2023,10 @@ public class P25DecoderState extends DecoderState
|
||||
TelephoneInterconnectChannelGrantExplicit ticge =
|
||||
(TelephoneInterconnectChannelGrantExplicit)pdu;
|
||||
|
||||
CallEvent callEvent = new P25CallEvent.Builder( CallEventType.CALL )
|
||||
CallEvent callEvent = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( ticge.getTransmitChannel() )
|
||||
.details( "Trigger: PDU-TEL_INT_V_CH_GRNT " +
|
||||
"TELEPHONE CALL " +
|
||||
( ticge.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
.details( ( ticge.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( ticge.isEmergency() ? " EMERGENCY" : "") +
|
||||
" CALL TIMER:" + ticge.getCallTimer() )
|
||||
.frequency( ticge.getDownlinkFrequency() )
|
||||
@@ -2017,11 +2101,10 @@ public class P25DecoderState extends DecoderState
|
||||
UnitToUnitVoiceChannelGrantExtended uuvcge =
|
||||
(UnitToUnitVoiceChannelGrantExtended)pdu;
|
||||
|
||||
CallEvent uuCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
|
||||
CallEvent uuCallEvent = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( uuvcge.getTransmitChannel() )
|
||||
.details( "Trigger:PDU-U2U_V-CH_GRNT " +
|
||||
( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
.details( ( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( uuvcge.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( uuvcge.getDownlinkFrequency() )
|
||||
.from( uuvcge.getSourceWACN() + "-" +
|
||||
@@ -2043,11 +2126,10 @@ public class P25DecoderState extends DecoderState
|
||||
UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue =
|
||||
(UnitToUnitVoiceChannelGrantUpdateExtended)pdu;
|
||||
|
||||
broadcast( new P25CallEvent.Builder( CallEventType.CALL )
|
||||
broadcast( new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( uuvcgue.getTransmitChannel() )
|
||||
.details( "Trigger: PDU-U2U_V_CH_G_U " +
|
||||
( uuvcgue.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
.details( ( uuvcgue.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( uuvcgue.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( uuvcgue.getDownlinkFrequency() )
|
||||
.from( uuvcgue.getSourceWACN() + "-" +
|
||||
@@ -2479,178 +2561,362 @@ public class P25DecoderState extends DecoderState
|
||||
*/
|
||||
private void processTSBKChannelGrant( TSBKMessage message )
|
||||
{
|
||||
P25CallEvent event = null;
|
||||
String channel = null;
|
||||
String from = null;
|
||||
String to = null;
|
||||
|
||||
switch( message.getOpcode() )
|
||||
{
|
||||
case GROUP_DATA_CHANNEL_GRANT:
|
||||
GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.DATA_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( gdcg.getChannel() )
|
||||
.details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gdcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gdcg.getDownlinkFrequency() )
|
||||
.from( gdcg.getSourceAddress() )
|
||||
.to( gdcg.getGroupAddress() )
|
||||
.build();
|
||||
channel = gdcg.getChannel();
|
||||
from = gdcg.getSourceAddress();
|
||||
to = gdcg.getGroupAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder( CallEventType.DATA_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gdcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gdcg.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
/* We don't allocate traffic channels for data */
|
||||
break;
|
||||
case GROUP_VOICE_CHANNEL_GRANT:
|
||||
GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( gvcg.getChannel() )
|
||||
.details( "Trigger: TSBK-G_V_CH_GRNT " +
|
||||
( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcg.getDownlinkFrequency() )
|
||||
.from( gvcg.getSourceAddress() )
|
||||
.to( gvcg.getGroupAddress() )
|
||||
.build();
|
||||
channel = gvcg.getChannel();
|
||||
from = gvcg.getSourceAddress();
|
||||
to = gvcg.getGroupAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcg.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
break;
|
||||
case GROUP_VOICE_CHANNEL_GRANT_UPDATE:
|
||||
GroupVoiceChannelGrantUpdate gvcgu =
|
||||
(GroupVoiceChannelGrantUpdate)message;
|
||||
|
||||
channel = gvcgu.getChannel1();
|
||||
from = null;
|
||||
to = gvcgu.getGroupAddress1();
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( gvcgu.getChannel1() )
|
||||
.details( "Trigger: TSBK-G_V_CH_GRNT_UP " +
|
||||
( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcgu.getDownlinkFrequency1() )
|
||||
.to( gvcgu.getGroupAddress1() )
|
||||
.build();
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.channel( gvcgu.getChannel1() )
|
||||
.details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcgu.getDownlinkFrequency1() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
|
||||
if( gvcgu.hasChannelNumber2() )
|
||||
{
|
||||
/* We handle this channel grant independent of the first event */
|
||||
P25CallEvent event2 = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( gvcgu.getChannel2() )
|
||||
.details( "Trigger: TSBK-G_V_CH_GRNT_UP#2 " +
|
||||
( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcgu.getDownlinkFrequency2() )
|
||||
.to( gvcgu.getGroupAddress2() )
|
||||
.build();
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this, event2 ) );
|
||||
channel = gvcgu.getChannel2();
|
||||
to = gvcgu.getGroupAddress2();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event2 = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( gvcgu.getChannel2() )
|
||||
.details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcgu.getDownlinkFrequency2() )
|
||||
.to( gvcgu.getGroupAddress2() )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event2 );
|
||||
broadcast( event2 );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
}
|
||||
break;
|
||||
case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT:
|
||||
GroupVoiceChannelGrantUpdateExplicit gvcgue =
|
||||
(GroupVoiceChannelGrantUpdateExplicit)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( gvcgue.getTransmitChannelIdentifier() + "-" +
|
||||
gvcgue.getTransmitChannelNumber() )
|
||||
.details( "Trigger: TSBK-G_V_CH_GRNT_U_E " +
|
||||
( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcgue.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcgue.getDownlinkFrequency() )
|
||||
.to( gvcgue.getGroupAddress() )
|
||||
.build();
|
||||
channel = gvcgue.getTransmitChannelIdentifier() + "-" +
|
||||
gvcgue.getTransmitChannelNumber();
|
||||
from = null;
|
||||
to = gvcgue.getGroupAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( gvcgue.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( gvcgue.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
break;
|
||||
case INDIVIDUAL_DATA_CHANNEL_GRANT:
|
||||
IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.DATA_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( idcg.getChannel() )
|
||||
.details( "Trigger: TSBK-IND_D_CH_GRNT " +
|
||||
( idcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( idcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( idcg.getDownlinkFrequency() )
|
||||
.from( idcg.getSourceAddress() )
|
||||
.to( idcg.getTargetAddress() )
|
||||
.build();
|
||||
channel = idcg.getChannel();
|
||||
from = idcg.getSourceAddress();
|
||||
to = idcg.getTargetAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder( CallEventType.DATA_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( idcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( idcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( idcg.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
/* We don't allocate a traffic channel for data */
|
||||
break;
|
||||
case SNDCP_DATA_CHANNEL_GRANT:
|
||||
SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.DATA_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( sdcg.getTransmitChannel() )
|
||||
.details( "Trigger: TSBK-SN_D_CH_GRNT " +
|
||||
"SNDCP DATA NSAPI:" + sdcg.getNSAPI() )
|
||||
.frequency( sdcg.getDownlinkFrequency() )
|
||||
.to( sdcg.getTargetAddress() )
|
||||
.build();
|
||||
channel = sdcg.getTransmitChannel();
|
||||
from = null;
|
||||
to = sdcg.getTargetAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder( CallEventType.DATA_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( sdcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( sdcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( sdcg.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
/* We don't allocate a traffic channel for data */
|
||||
break;
|
||||
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT:
|
||||
TelephoneInterconnectVoiceChannelGrant tivcg =
|
||||
(TelephoneInterconnectVoiceChannelGrant)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( tivcg.getChannel() )
|
||||
.details( "Trigger: TSBK-T_INT_V_CH_GRNT " +
|
||||
( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( tivcg.isEmergency() ? " EMERGENCY" : "") +
|
||||
" CALL TIMER:" + tivcg.getCallTimer() )
|
||||
.frequency( tivcg.getDownlinkFrequency() )
|
||||
.from( tivcg.getAddress() )
|
||||
.build();
|
||||
channel = tivcg.getChannel();
|
||||
from = null;
|
||||
/* Address is ambiguous and could mean either source or target,
|
||||
* so we'll place the value in the to field */
|
||||
to = tivcg.getAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder(
|
||||
CallEventType.TELEPHONE_INTERCONNECT )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( tivcg.isEncrypted() ? "ENCRYPTED " : "" ) +
|
||||
( tivcg.isEmergency() ? "EMERGENCY " : "") +
|
||||
"CALL TIMER:" + tivcg.getCallTimer() )
|
||||
.frequency( tivcg.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
break;
|
||||
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE:
|
||||
TelephoneInterconnectVoiceChannelGrantUpdate tivcgu =
|
||||
(TelephoneInterconnectVoiceChannelGrantUpdate)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( tivcgu.getChannelIdentifier() + "-" +
|
||||
tivcgu.getChannelNumber() )
|
||||
.details( "Trigger: TSBK-T_INT_V_CH_G_UP " +
|
||||
( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( tivcgu.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( tivcgu.getDownlinkFrequency() )
|
||||
.from( tivcgu.getAddress() )
|
||||
.build();
|
||||
channel = tivcgu.getChannelIdentifier() + "-" +
|
||||
tivcgu.getChannelNumber();
|
||||
from = null;
|
||||
|
||||
/* Address is ambiguous and could mean either source or target,
|
||||
* so we'll place the value in the to field */
|
||||
to = tivcgu.getAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder(
|
||||
CallEventType.TELEPHONE_INTERCONNECT )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( tivcgu.isEncrypted() ? "ENCRYPTED " : "" ) +
|
||||
( tivcgu.isEmergency() ? "EMERGENCY " : "") +
|
||||
"CALL TIMER:" + tivcgu.getCallTimer() )
|
||||
.frequency( tivcgu.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
break;
|
||||
case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT:
|
||||
UnitToUnitVoiceChannelGrant uuvcg =
|
||||
(UnitToUnitVoiceChannelGrant)message;
|
||||
|
||||
channel = uuvcg.getChannelIdentifier() + "-" +
|
||||
uuvcg.getChannelNumber();
|
||||
from = uuvcg.getSourceAddress();
|
||||
to = uuvcg.getTargetAddress();
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( uuvcg.getChannelIdentifier() + "-" +
|
||||
uuvcg.getChannelNumber() )
|
||||
.details( "Trigger: TSBK-U2U_V_CH_GRNT " +
|
||||
( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( uuvcg.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( uuvcg.getDownlinkFrequency() )
|
||||
.from( uuvcg.getSourceAddress() )
|
||||
.to( uuvcg.getTargetAddress() )
|
||||
.build();
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder(
|
||||
CallEventType.UNIT_TO_UNIT_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( uuvcg.isEncrypted() ? "ENCRYPTED " : "" ) +
|
||||
( uuvcg.isEmergency() ? "EMERGENCY " : "") )
|
||||
.frequency( uuvcg.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
break;
|
||||
case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE:
|
||||
UnitToUnitVoiceChannelGrantUpdate uuvcgu =
|
||||
(UnitToUnitVoiceChannelGrantUpdate)message;
|
||||
|
||||
event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( uuvcgu.getChannelIdentifier() + "-" +
|
||||
uuvcgu.getChannelNumber() )
|
||||
.details( "Trigger: TSBK-U2U_V_CH_G_UP " +
|
||||
( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
|
||||
( uuvcgu.isEmergency() ? " EMERGENCY" : "") )
|
||||
.frequency( uuvcgu.getDownlinkFrequency() )
|
||||
.from( uuvcgu.getSourceAddress() )
|
||||
.to( uuvcgu.getTargetAddress() )
|
||||
.build();
|
||||
channel = uuvcgu.getChannelIdentifier() + "-" +
|
||||
uuvcgu.getChannelNumber();
|
||||
from = uuvcgu.getSourceAddress();
|
||||
to = uuvcgu.getTargetAddress();
|
||||
|
||||
if( hasCallEvent( channel, from, to ) )
|
||||
{
|
||||
updateCallEvent( channel, from, to );
|
||||
}
|
||||
else
|
||||
{
|
||||
P25CallEvent event = new P25CallEvent.Builder(
|
||||
CallEventType.UNIT_TO_UNIT_CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( channel )
|
||||
.details( ( uuvcgu.isEncrypted() ? "ENCRYPTED " : "" ) +
|
||||
( uuvcgu.isEmergency() ? "EMERGENCY " : "") )
|
||||
.frequency( uuvcgu.getDownlinkFrequency() )
|
||||
.from( from )
|
||||
.to( to )
|
||||
.build();
|
||||
|
||||
registerCallEvent( event );
|
||||
broadcast( event );
|
||||
}
|
||||
|
||||
broadcast( new TrafficChannelAllocationEvent( this,
|
||||
mChannelCallMap.get( channel ) ) );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( event != null && event.getCallEventType() != CallEventType.DATA_CALL )
|
||||
{
|
||||
broadcast( new TrafficChannelAllocationEvent( this, event ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2658,38 +2924,35 @@ public class P25DecoderState extends DecoderState
|
||||
*/
|
||||
private void updateTo( String to )
|
||||
{
|
||||
mLog.debug( "Updating to [" + to + "]" );
|
||||
if( to != null &&
|
||||
!to.contentEquals( "0000" ) &&
|
||||
!to.contentEquals( "000000" ) &&
|
||||
( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) )
|
||||
if( to != null && !to.contentEquals( "0000" ) && !to.contentEquals( "000000" ) )
|
||||
{
|
||||
mToTalkgroup = to;
|
||||
broadcast( ChangedAttribute.TO_TALKGROUP );
|
||||
if( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) )
|
||||
{
|
||||
mToTalkgroup = to;
|
||||
broadcast( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
if( mCurrentCallEvent != null &&
|
||||
( mCurrentCallEvent.getToID() == null ||
|
||||
!mCurrentCallEvent.getToID().contentEquals( to ) ) )
|
||||
{
|
||||
mCurrentCallEvent.setToID( to );
|
||||
broadcast( mCurrentCallEvent );
|
||||
}
|
||||
if( mCurrentCallEvent != null &&
|
||||
( mCurrentCallEvent.getToID() == null ||
|
||||
!mCurrentCallEvent.getToID().contentEquals( to ) ) )
|
||||
{
|
||||
mCurrentCallEvent.setToID( to );
|
||||
broadcast( mCurrentCallEvent );
|
||||
}
|
||||
|
||||
if( hasAliasList() )
|
||||
{
|
||||
mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup );
|
||||
mLog.debug( "To Alias: " + mToAlias );
|
||||
if( hasAliasList() )
|
||||
{
|
||||
mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup );
|
||||
}
|
||||
else
|
||||
{
|
||||
mToAlias = null;
|
||||
}
|
||||
|
||||
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.debug( "To Alias: no alias list" );
|
||||
mToAlias = null;
|
||||
}
|
||||
|
||||
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
|
||||
/* Send audio metadata update */
|
||||
broadcast( new Metadata( MetadataType.TO, to, mToAlias, true ) );
|
||||
broadcast( new Metadata( MetadataType.TO, mToTalkgroup, mToAlias, true ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2698,35 +2961,35 @@ public class P25DecoderState extends DecoderState
|
||||
*/
|
||||
private void updateFrom( String from )
|
||||
{
|
||||
if( from != null &&
|
||||
!from.contentEquals( "0000" ) &&
|
||||
!from.contentEquals( "000000" ) &&
|
||||
( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) )
|
||||
if( from != null && !from.contentEquals( "0000" ) && !from.contentEquals( "000000" ) )
|
||||
{
|
||||
mFromTalkgroup = from;
|
||||
broadcast( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
if( mCurrentCallEvent != null &&
|
||||
mCurrentCallEvent.getFromID() == null ||
|
||||
!mCurrentCallEvent.getFromID().contentEquals( from ) )
|
||||
if( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ))
|
||||
{
|
||||
mCurrentCallEvent.setFromID( from );
|
||||
broadcast( mCurrentCallEvent );
|
||||
mFromTalkgroup = from;
|
||||
broadcast( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
if( mCurrentCallEvent != null &&
|
||||
mCurrentCallEvent.getFromID() == null ||
|
||||
!mCurrentCallEvent.getFromID().contentEquals( from ) )
|
||||
{
|
||||
mCurrentCallEvent.setFromID( from );
|
||||
broadcast( mCurrentCallEvent );
|
||||
}
|
||||
|
||||
if( hasAliasList() )
|
||||
{
|
||||
mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFromAlias = null;
|
||||
}
|
||||
|
||||
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
if( hasAliasList() )
|
||||
{
|
||||
mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFromAlias = null;
|
||||
}
|
||||
|
||||
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
|
||||
/* Send audio metadata update */
|
||||
broadcast( new Metadata( MetadataType.FROM, from, mFromAlias, true ) );
|
||||
broadcast( new Metadata( MetadataType.FROM, mFromTalkgroup, mFromAlias, true ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ public class P25AudioModule extends Module implements Listener<Message>,
|
||||
private int mSourceID;
|
||||
private static boolean mLibraryLoadStatusLogged = false;
|
||||
private boolean mCanConvertAudio = false;
|
||||
private boolean mEncryptedAudio = false;
|
||||
private AudioConverter mAudioConverter;
|
||||
private Listener<AudioPacket> mAudioPacketListener;
|
||||
private AudioMetadata mAudioMetadata;
|
||||
@@ -105,26 +104,17 @@ public class P25AudioModule extends Module implements Listener<Message>,
|
||||
/* If we detect a valid LDU message with the encryption flag
|
||||
* set to true, we toggle the encrypted audio state until we
|
||||
* receive the first TDU or TDULC message, then reset it. */
|
||||
if( ldu.isValid() && ldu.isEncrypted() )
|
||||
{
|
||||
mEncryptedAudio = true;
|
||||
}
|
||||
|
||||
if( !mEncryptedAudio && mAudioConverter != null )
|
||||
if( !( ldu.isValid() && ldu.isEncrypted() ) )
|
||||
{
|
||||
for( byte[] frame: ((LDUMessage)message).getIMBEFrames() )
|
||||
{
|
||||
float[] audio = mAudioConverter.decode( frame );
|
||||
|
||||
|
||||
mAudioPacketListener.receive(
|
||||
new AudioPacket( audio, mAudioMetadata ) );
|
||||
new AudioPacket( audio, mAudioMetadata.copyOf() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( message instanceof TDUMessage || message instanceof TDULinkControlMessage )
|
||||
{
|
||||
mEncryptedAudio = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,10 @@ public class LDU2Message extends LDUMessage
|
||||
@Override
|
||||
public boolean isEncrypted()
|
||||
{
|
||||
return getEncryption() != Encryption.UNENCRYPTED;
|
||||
Encryption encryption = getEncryption();
|
||||
|
||||
return encryption != Encryption.UNENCRYPTED &&
|
||||
encryption != Encryption.UNKNOWN;
|
||||
}
|
||||
|
||||
private void checkCRC()
|
||||
|
||||
@@ -22,11 +22,11 @@ public class MotorolaTSBKMessage extends TSBKMessage
|
||||
sb.append( " " );
|
||||
sb.append( getDUID().getLabel() );
|
||||
|
||||
sb.append( " MOTOROLA" );
|
||||
sb.append( " MOTOROLA " );
|
||||
|
||||
if( isEncrypted() )
|
||||
{
|
||||
sb.append( " ENCRYPTED" );
|
||||
sb.append( " ENCRYPTED " );
|
||||
}
|
||||
|
||||
sb.append( getMotorolaOpcode().getLabel() );
|
||||
|
||||
@@ -241,7 +241,14 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
*/
|
||||
private void updateResetTimeout()
|
||||
{
|
||||
mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
|
||||
if( mChannelType == ChannelType.TRAFFIC )
|
||||
{
|
||||
mResetTimeout = System.currentTimeMillis();
|
||||
}
|
||||
else
|
||||
{
|
||||
mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -376,61 +376,64 @@ public class SettingsManager
|
||||
*/
|
||||
public ImageIcon getImageIcon( String name, int height )
|
||||
{
|
||||
String mergedName = name + height;
|
||||
|
||||
if( mResizedIcons.containsKey( mergedName ) )
|
||||
if( name != null )
|
||||
{
|
||||
return mResizedIcons.get( mergedName );
|
||||
}
|
||||
|
||||
MapIcon mapIcon = mIcons.get( name );
|
||||
|
||||
if( mapIcon != null )
|
||||
{
|
||||
if( mapIcon.getImageIcon().getIconHeight() > height )
|
||||
String mergedName = name + height;
|
||||
|
||||
if( mResizedIcons.containsKey( mergedName ) )
|
||||
{
|
||||
ImageIcon scaled = getScaledIcon( mapIcon.getImageIcon(), height );
|
||||
|
||||
mResizedIcons.put( mergedName, scaled );
|
||||
|
||||
return scaled;
|
||||
return mResizedIcons.get( mergedName );
|
||||
}
|
||||
else
|
||||
{
|
||||
mResizedIcons.put( mergedName, mapIcon.getImageIcon() );
|
||||
}
|
||||
}
|
||||
|
||||
MapIcon mapIcon = mIcons.get( name );
|
||||
|
||||
/* Use the default Icon */
|
||||
String mergedDefault = mCurrentDefaultIconName + height;
|
||||
|
||||
if( mResizedIcons.containsKey( mergedDefault ) )
|
||||
{
|
||||
return mResizedIcons.get( mergedDefault );
|
||||
}
|
||||
else
|
||||
{
|
||||
MapIcon defaultIcon = mIcons.get( mCurrentDefaultIconName );
|
||||
|
||||
if( defaultIcon != null )
|
||||
if( mapIcon != null )
|
||||
{
|
||||
if( defaultIcon.getImageIcon().getIconHeight() > height )
|
||||
if( mapIcon.getImageIcon().getIconHeight() > height )
|
||||
{
|
||||
ImageIcon scaledDefault = getScaledIcon( defaultIcon.getImageIcon(), height );
|
||||
mResizedIcons.put( mergedDefault, scaledDefault );
|
||||
return scaledDefault;
|
||||
ImageIcon scaled = getScaledIcon( mapIcon.getImageIcon(), height );
|
||||
|
||||
mResizedIcons.put( mergedName, scaled );
|
||||
|
||||
return scaled;
|
||||
}
|
||||
else
|
||||
{
|
||||
mResizedIcons.put( mergedDefault, defaultIcon.getImageIcon() );
|
||||
return defaultIcon.getImageIcon();
|
||||
mResizedIcons.put( mergedName, mapIcon.getImageIcon() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Something happened ... the above should always return an icon */
|
||||
mLog.error( "SettingsManager - couldn't return an icon named [" +
|
||||
name + "] of heigh [" + height + "]" );
|
||||
/* Use the default Icon */
|
||||
String mergedDefault = mCurrentDefaultIconName + height;
|
||||
|
||||
if( mResizedIcons.containsKey( mergedDefault ) )
|
||||
{
|
||||
return mResizedIcons.get( mergedDefault );
|
||||
}
|
||||
else
|
||||
{
|
||||
MapIcon defaultIcon = mIcons.get( mCurrentDefaultIconName );
|
||||
|
||||
if( defaultIcon != null )
|
||||
{
|
||||
if( defaultIcon.getImageIcon().getIconHeight() > height )
|
||||
{
|
||||
ImageIcon scaledDefault = getScaledIcon( defaultIcon.getImageIcon(), height );
|
||||
mResizedIcons.put( mergedDefault, scaledDefault );
|
||||
return scaledDefault;
|
||||
}
|
||||
else
|
||||
{
|
||||
mResizedIcons.put( mergedDefault, defaultIcon.getImageIcon() );
|
||||
return defaultIcon.getImageIcon();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Something happened ... the above should always return an icon */
|
||||
mLog.error( "SettingsManager - couldn't return an icon named [" +
|
||||
name + "] of heigh [" + height + "]" );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,18 @@ public abstract class TunerController implements Tunable
|
||||
{
|
||||
return mFrequencyController.getMaximumFrequency();
|
||||
}
|
||||
|
||||
public long getMinTunedFrequency()
|
||||
{
|
||||
return mFrequencyController.getFrequency() -
|
||||
( mFrequencyController.getBandwidth() / 2 );
|
||||
}
|
||||
|
||||
public long getMaxTunedFrequency()
|
||||
{
|
||||
return mFrequencyController.getFrequency() +
|
||||
( mFrequencyController.getBandwidth() / 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the tuner can accomodate this new channel frequency and
|
||||
|
||||
@@ -4,9 +4,11 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -27,6 +29,9 @@ import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import source.SourceException;
|
||||
import source.tuner.Tuner;
|
||||
import source.tuner.TunerChannel;
|
||||
import source.tuner.TunerChannelSource;
|
||||
import source.tuner.TunerConfiguration;
|
||||
import source.tuner.TunerController;
|
||||
import controller.ThreadPoolManager;
|
||||
@@ -826,6 +831,91 @@ public class AirspyTunerController extends TunerController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the tuner can accomodate this new channel frequency and
|
||||
* bandwidth, along with all of the existing tuned channels currently in
|
||||
* place.
|
||||
*/
|
||||
public boolean canTuneChannel( TunerChannel channel )
|
||||
{
|
||||
//If this is the first lock, then we're good
|
||||
if( mTunedChannels.isEmpty() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Sort the existing locks and get the min/max locked frequencies
|
||||
Collections.sort( mTunedChannels );
|
||||
|
||||
long minLockedFrequency = mTunedChannels.get( 0 ).getMinFrequency();
|
||||
long maxLockedFrequency = mTunedChannels
|
||||
.get( mTunedChannels.size() - 1 ).getMaxFrequency();
|
||||
|
||||
if( channel.getMinFrequency() >= minLockedFrequency &&
|
||||
channel.getMaxFrequency() <= maxLockedFrequency )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( channel.getMinFrequency() < minLockedFrequency )
|
||||
{
|
||||
return ( maxLockedFrequency - channel.getMinFrequency() ) < getBandwidth() - 1E5;
|
||||
}
|
||||
|
||||
if( channel.getMaxFrequency() > maxLockedFrequency )
|
||||
{
|
||||
return ( channel.getMaxFrequency() - minLockedFrequency ) <= getBandwidth() - 1E5;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager,
|
||||
Tuner tuner, TunerChannel tunerChannel )
|
||||
throws RejectedExecutionException, SourceException
|
||||
{
|
||||
TunerChannelSource source = null;
|
||||
|
||||
if( canTuneChannel( tunerChannel ) )
|
||||
{
|
||||
mTunedChannels.add( tunerChannel );
|
||||
|
||||
Collections.sort( mTunedChannels );
|
||||
|
||||
long min = mTunedChannels.get( 0 ).getMinFrequency();
|
||||
long max = mTunedChannels.get( mTunedChannels.size() - 1 ).getMaxFrequency();
|
||||
|
||||
if( !( getMinTunedFrequency() <= min &&
|
||||
max <= getMaxTunedFrequency() ) )
|
||||
{
|
||||
long freq = min + ( mFrequencyController.getBandwidth() / 2 ) + 50000;
|
||||
|
||||
mFrequencyController.setFrequency( freq );
|
||||
}
|
||||
|
||||
source = new TunerChannelSource( threadPoolManager,
|
||||
tuner, tunerChannel );
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public void releaseChannel( TunerChannelSource tunerChannelSource )
|
||||
{
|
||||
if( tunerChannelSource != null )
|
||||
{
|
||||
mTunedChannels.remove( tunerChannelSource.getTunerChannel() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( "Tuner Controller - couldn't find the tuned channel "
|
||||
+ "to release it" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Airspy Board Identifier
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user