Skip to content

Commit

Permalink
#1981 Adds P25 Phase1 support for foreign system IDEN_UPD_TDMA messag…
Browse files Browse the repository at this point in the history
…es on control channel. (#1982)

Co-authored-by: Dennis Sheirer <[email protected]>
  • Loading branch information
DSheirer and Dennis Sheirer authored Sep 17, 2024
1 parent 056bccb commit 6f05db7
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,10 @@ private void processAMBTC(P25P1Message message)
urr.getResponse() + " UNIT REGISTRATION");
}
break;
case OSP_IDENTIFIER_UPDATE_TDMA:
//Ignore - in the extended format it is carrying a frequency band for a foreign system and we
//don't allow that to corrupt the real frequency bands for this system.
break;
default:
// mLog.debug("Unrecognized AMBTC Opcode: " + ambtc.getHeader().getOpcode().name());
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.isp.AMBTCUnitToUnitVoiceServiceRequest;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCAdjacentStatusBroadcast;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCCallAlert;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCFrequencyBandUpdateTDMA;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCGroupAffiliationQuery;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCGroupAffiliationResponse;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.osp.AMBTCGroupDataChannelGrant;
Expand Down Expand Up @@ -245,6 +246,8 @@ public static P25P1Message createAMBTC(PDUSequence pduSequence, int nac, long ti
return new AMBTCGroupAffiliationResponse(pduSequence, nac, timestamp);
case OSP_GROUP_VOICE_CHANNEL_GRANT:
return new AMBTCGroupVoiceChannelGrant(pduSequence, nac, timestamp);
case OSP_IDENTIFIER_UPDATE_TDMA:
return new AMBTCFrequencyBandUpdateTDMA(pduSequence, nac, timestamp);
case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT:
return new AMBTCIndividualDataChannelGrant(pduSequence, nac, timestamp);
case OSP_MESSAGE_UPDATE:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.p25.phase1.message.pdu.ambtc.osp;

import io.github.dsheirer.bits.FragmentedIntField;
import io.github.dsheirer.bits.IntField;
import io.github.dsheirer.bits.LongField;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.module.decode.p25.identifier.APCO25System;
import io.github.dsheirer.module.decode.p25.identifier.APCO25Wacn;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUSequence;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
import io.github.dsheirer.module.decode.p25.reference.ChannelType;
import java.util.Collections;
import java.util.List;
import org.apache.commons.math3.util.FastMath;

/**
* Frequency band update TDMA multi-block format for a foreign system.
*
* Note: removed the IFrequencyBand interface for this message so that it doesn't get processed as a frequency band for
* the current system which would likely collide with the real frequency band for the current system.
*/
public class AMBTCFrequencyBandUpdateTDMA extends AMBTCMessage // implements IFrequencyBand
{
private static final IntField HEADER_IDENTIFIER = IntField.length4(OCTET_3_BIT_24);
private static final IntField HEADER_CHANNEL_TYPE = IntField.length4(OCTET_3_BIT_24 + 4);
private static final FragmentedIntField HEADER_WACN = FragmentedIntField.of(32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 64, 65, 66, 67);
private static final IntField HEADER_SYSTEM = IntField.length12(OCTET_8_BIT_64 + 4);
private static final LongField BLOCK_0_BASE_FREQUENCY = LongField.length32(OCTET_0_BIT_0);
private static final int BLOCK_0_TRANSMIT_OFFSET_SIGN = OCTET_4_BIT_32;
private static final IntField BLOCK_0_TRANSMIT_OFFSET = IntField.range(OCTET_4_BIT_32 + 1, OCTET_4_BIT_32 + 13);
private static final IntField BLOCK_0_CHANNEL_SPACING = IntField.range(OCTET_5_BIT_40 + 6, OCTET_5_BIT_40 + 15);

private ChannelType mChannelType;
private Identifier mWacn;
private Identifier mSystem;

/**
* Constructs an instance
* @param PDUSequence containing the header and block 0
* @param nac for the system
* @param timestamp of the PDU sequence
*/
public AMBTCFrequencyBandUpdateTDMA(PDUSequence PDUSequence, int nac, long timestamp)
{
super(PDUSequence, nac, timestamp);
}

public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(getMessageStub());
sb.append(" WACN:").append(getWacn());
sb.append(" FOREIGN SYSTEM:").append(getSystem());
sb.append(" ID:").append(getIdentifier());
sb.append(" OFFSET:").append(getTransmitOffset());
sb.append(" SPACING:").append(getChannelSpacing());
sb.append(" BASE:").append(getBaseFrequency());
sb.append(" ").append(getChannelType());
return sb.toString();
}

public Identifier getWacn()
{
if(mWacn == null)
{
mWacn = APCO25Wacn.create(getHeader().getMessage().getInt(HEADER_WACN));
}

return mWacn;
}

public Identifier getSystem()
{
if(mSystem == null)
{
mSystem = APCO25System.create(getHeader().getMessage().getInt(HEADER_SYSTEM));
}

return mSystem;
}

public ChannelType getChannelType()
{
if(mChannelType == null)
{
mChannelType = ChannelType.fromValue(getHeader().getMessage().getInt(HEADER_CHANNEL_TYPE));
}

return mChannelType;
}

public int getIdentifier()
{
return getHeader().getMessage().getInt(HEADER_IDENTIFIER);
}

public long getChannelSpacing()
{
if(hasDataBlock(0))
{
return getDataBlock(0).getMessage().getInt(BLOCK_0_CHANNEL_SPACING) * 125L;
}

return 0;
}

public long getBaseFrequency()
{
if(hasDataBlock(0))
{
return getDataBlock(0).getMessage().getLong(BLOCK_0_BASE_FREQUENCY) * 5L;
}

return 0L;
}

public int getBandwidth()
{
return getChannelType().getBandwidth();
}

public long getTransmitOffset()
{
if(hasDataBlock(0))
{
long offset = getDataBlock(0).getMessage().getInt(BLOCK_0_TRANSMIT_OFFSET) * getChannelSpacing();

if(!getDataBlock(0).getMessage().get(BLOCK_0_TRANSMIT_OFFSET_SIGN))
{
offset *= -1;
}

return offset;
}

return 0L;
}

/**
* Indicates if the frequency band has a transmit option for the subscriber unit.
*/
public boolean hasTransmitOffset()
{
return hasDataBlock(0) && getDataBlock(0).getMessage().getInt(BLOCK_0_TRANSMIT_OFFSET) != 0x80;
}

public long getDownlinkFrequency(int channelNumber)
{
return getBaseFrequency() + (getChannelSpacing() * (int)(FastMath.floor(channelNumber / getTimeslotCount())));
}

public long getUplinkFrequency(int channelNumber)
{
if(hasTransmitOffset())
{
return getDownlinkFrequency(channelNumber) + getTransmitOffset();
}

return 0;
}

public boolean isTDMA()
{
return getChannelType().isTDMA();
}

public int getTimeslotCount()
{
return getChannelType().getSlotsPerCarrier();
}

public List<Identifier> getIdentifiers()
{
return Collections.EMPTY_LIST;
}
}

0 comments on commit 6f05db7

Please sign in to comment.