Some radios set the encryption flag in the service options of the LDU1 Group
Voice Channel User link control while transmitting clear voice; the HDU and
LDU2 encryption sync parameters report ALGID 0x80 (unencrypted) for the same
call. Example (CoMIRS Boston, TG 2217, valid LCs from several radios):
HDU TALKGROUP:2217 UNENCRYPTED
LDU1 GROUP VOICE CHANNEL USER FM:64008 TO:2217 SERVICE OPTIONS:PRI4 ENCRYPTED CIRCUIT
LDU2 UNENCRYPTED
P25P1DecoderState broadcast State.ENCRYPTED for each such LDU1 and State.CALL
for each LDU2, so the channel state flapped between CALL and ENCRYPTED (and on
upstream, where ENCRYPTED cannot change back to CALL, the call stays muted).
Track the per-call encryption state from the HDU header and LDU2 algorithm ID
(reset at TDU/TDULC). When it is known to be unencrypted, clear the encryption
flag on the LC service options before using them for the channel state and
traffic channel call event. Genuinely encrypted calls are unaffected, and when
the algorithm ID is not yet known (late entry before the first LDU2) the LC
service options are used as before.
Adds VoiceServiceOptions.createUnencrypted(ServiceOptions) to copy service
options with only the encryption flag cleared.
Verified by driving P25P1DecoderState with HDU/LDU1/LDU2/TDU sequences
reproducing the field logs: state events go from C C C E C E C to all CALL;
encrypted calls (ALGID 0x84) still report ENCRYPTED throughout.
Claude-Session: https://claude.ai/code/session_01FRdfTvgcdBvwkipdfP8qxy
(cherry picked from commit 2ef3c9d02176871577d38f8afd0fa3c32fb92a58)
Co-authored-by: Claude <noreply@anthropic.com>
PDU header format 0x15 is the generic unconfirmed data header; unconfirmed
multi-block trunking control (UMBTC) is only its trunking control SAP
(61/63) use. PDUHeaderFactory built a plain PDUHeader for every 0x15
header and PDUMessageFactory sent every such sequence to createUMBTC(), so
outbound unconfirmed IP data (SNDCP RF UNCONFIRMED DATA with SAP 0, 4 or
6) was logged raw as
PDU HEADER FORMAT:UNCONFIRMED MBTC UNCONFIRMED VENDOR:STANDARD TO LLID:n
DATA BLOCKS:4 MSG:4100450000...
while the confirmed equivalent (format 0x16) was decoded as IPPKT with
its LRRP / ARS payload.
- PDUHeaderFactory: build a PacketHeader for a 0x15 header whose SAP is
UNENCRYPTED_USER_DATA, PACKET_DATA or SNDCP_PACKET_DATA_CONTROL. Other
SAPs, including trunking control 61/63, keep the plain PDUHeader.
- PDUMessageFactory: route a 0x15 sequence whose header is a PacketHeader
through createPacketData(); everything else stays on the UMBTC path.
No payload changes are needed: PacketMessage already sizes unconfirmed
data blocks at 12 octets, and the header fields it uses (SAP, pad octet
count, data header offset) sit at the same bit positions in the confirmed
and unconfirmed headers. The ARS RegistrationAcknowledgement parser only
reads the extension octet, so the 5-octet trailer (0x04 + 32-bit time)
some systems append does not affect it.
Adds PDUMessageFactoryTest, built from two unconfirmed PDUs captured on a
Motorola P25 Phase 1 system (ARS registration acknowledgements to UDP
port 4005, one with the trailer), plus header routing checks for SAP
0/4/6, SAP 61/63 and the confirmed 0x16 header.
Co-authored-by: csmith_ai <carrottop@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Motorola LRRP request tokens that carry an integer (trigger interval
0x31, trigger distance 0x78 and the unknown parameters 0x42 / 0x44)
encode it as a 7-bit variable length integer: each byte holds seven
value bits, most significant group first, and the high bit of a byte
flags a continuation byte. The parser read a single byte, so a real
triggered location start request
09 11 22 03 FF FF EE 52 44 64 42 9C 10 62 57 34 31 84 58
was logged as
ID:15663103 TRIGGER ON GPIO TRIGGER EVERY:132 SECS REQUESTED TOKENS
[TIME,REQUEST-64,REQUEST-62,HORIZONTAL DIRECTION,TIMESTAMP,
UNRECOGNIZED(10,44,58,9C)]
and now decodes to
ID:FFFFEE PERIODIC INTERVAL:600 SECS PARAM-42:3600 PARAM-44:100
REQUESTED TOKENS [TIME,HORIZONTAL SPEED,HORIZONTAL DIRECTION]
The encoding and the token names follow node-dmr-lib (LRRP.js
parseLRRPInt/toLRRPInt and its quoted TRBONet captures, DMRConst.js)
and Moto.Net (LRRPPacket.cs "using VLQ for most of the fields").
- Add VarInt, a shared 7-bit varint reader, and VarIntToken, a token
base class whose byte length is measured from the value and bounded
by the remaining payload. Use them for 0x31, 0x78, 0x42 and 0x44.
- Add TokenType.fromRequestValue(): 0x34 is the periodic trigger flag
in a request and the timestamp in a response. TokenFactory accepts
the resolved TokenType so the request path is not re-resolved.
- Rename request tokens: 0x31 TRIGGER_INTERVAL (TriggerInterval),
0x4A TRIGGER_CONDITION (TriggerCondition: 02 emergency, 42 pin
status change, 50 PTT, 51 PTT emergency), 0x78 TRIGGER_DISTANCE
(TriggerDistance), 0x62 HORIZONTAL_SPEED. 0x42 and 0x44 become
REQUEST_PARAMETER_42/44 (RequestParameter) with an optional varint
value; their meaning is unknown (observed 3600 and 100). Remove
TriggerGpio and TriggerOnMove.
- Identity: assemble the request id big-endian (it was accumulated
LSB-first), render it in hex, and clamp the read to the message.
- RequestedTokens: keep a list that holds only unrecognized ids; add
getters for the recognized and unrecognized lists.
- Remove leftover debug locals from LRRPPacket.parseTokens().
- Add LRRPPacketTest (both captured bodies plus two copies with a
corrupted type byte) and VarIntTest.
Built with the project's Gradle build (JDK 27 toolchain, in Docker);
compileJava, compileTestJava and the 9 new tests pass.
Co-authored-by: csmith_ai <carrottop@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
GOLAY_WORD_STARTS listed 278 where it should list 378. The word at 378 was
never corrected, and the decoder ran a correction over bits 278-295 that
straddle the already corrected words at 270 and 288.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Golay24.checkAndCorrect indexed the word from bit 0 (getInt(0, 22), xor(12, ...),
flip(index)) and took the parity over the whole message, so it was only right for a
message that is exactly one 24-bit word. P25 TDULC words 2-12 and the DMR voice
superframe words at 24 and 48 were "corrected" in the wrong bits. Decode any other
word in a 24-bit copy and write it back.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A lagging sync detect on the first symbol after a buffer reload, followed by a
coarse timing search that steps all the way left, scores a candidate whose first
sync symbol is one sample before the buffer start. score() only checked the end
of the buffer, so it threw ArrayIndexOutOfBoundsException: Index -1.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Uses libhydrasdr HIGH_DEFINITION decimation mode by default.
- Tuner controller extends TunerController since libhydrasdr owns the USB device, transfers and real to complex sample conversion.
- Tuners are still discovered by USB bus and port. The controller reads the USB serial string and opens the device by serial number.
- Samples are scaled by 2 to preserve existing levels (-6 dB otherwise).
- Streaming is stopped for sample rate changes because libhydrasdr replaces the sample converter in use by its streaming thread.
- Buffer sample count follows the delivered power-of-2 buffer size and adds the 1.25 MHz sample rate.
Co-authored-by: denny <dennis.sheirer@gmail.com>
* #2535 Heterodyne Channelizer enhancements and corrections.
* #2537 Complex vector oscillator SIMD lane drift.
Realigns the SIMD lanes every 262,144 samples. At frequencies where the
rotation for a set of lanes is nearly a whole or half turn, rounding affects
each lane differently and the lanes drift apart until the next frequency
change, producing spurs at multiples of the sample rate divided by the lane
count. Lane phase error at 3125 Hz with a 50 kHz sample rate was about -38 dBc
after one hour and is now about -83 dBc with 16 lanes and -73 dBc with 8 lanes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #2537 test updates
* #2537 test updates
---------
Co-authored-by: denny <dennis.sheirer@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Persist the per-channel selection, preserve it when copying configurations, and transfer it to mixed-phase P25 traffic channels. Apply only when the loaded codec advertises support. Calibrated levels remain the default.
- Fix two-channel synthesis asymmetry across polyphase channel boundaries.
- TwoChannelSynthesizerM2: replace incorrect 2-path polyphase split (which applied
different responses to each channel) with channel sum + prototype filter
modulated to the channel boundary (+FS/4).
- ComplexPolyphaseChannelizerM2: start on the middle block map when taps per
channel is odd so adjacent channel outputs are phase coherent.
- TwoChannelOutputProcessor: translate channel 2 by FS/2 using the absolute
block index parity derived from the even-sized dispatch batches.
- SynthesizerViewer: translate channel 2 before synthesis.
- Add TwoChannelSynthesizerM2Test for flat, symmetric boundary response.
Co-authored-by: denny <dennis.sheirer@gmail.com>
* Adopts same architecture and symbol processor as P1 C4FM/LSM, DMR and NXDN.
* Soft symbol sync detection, processing and erro correction.
* Scalar and vector soft sync detection and calibration
* Improved time slot detection/prediction and ISCH processing.
* Fixes bug in parsing sequential mac structures in SACCH
* Vector calibration(s) cleanup across multiple calibrations
* Error correction bug in ISCH
* Renames differential demod shared with P1 LSM decoder.
Co-authored-by: denny <dennis.sheirer@gmail.com>