diff --git a/src/main/java/io/github/dsheirer/dsp/c4fm/C4FMSymbolProcessor.java b/src/main/java/io/github/dsheirer/dsp/c4fm/C4FMSymbolProcessor.java index 4bf5c346..b23cfcaf 100644 --- a/src/main/java/io/github/dsheirer/dsp/c4fm/C4FMSymbolProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/c4fm/C4FMSymbolProcessor.java @@ -1313,7 +1313,7 @@ public abstract class C4FMSymbolProcessor for(int x = 0; x < patternSymbols.length; x++) { - if(bufferPointer < maxPointer) + if(bufferPointer >= 0 && bufferPointer < maxPointer) { softSymbol = interpolate(mBuffer[bufferPointer], mBuffer[bufferPointer + 1], fractional) * gain + balance; } diff --git a/src/test/java/io/github/dsheirer/dsp/c4fm/C4FMSymbolProcessorScoreTest.java b/src/test/java/io/github/dsheirer/dsp/c4fm/C4FMSymbolProcessorScoreTest.java new file mode 100644 index 00000000..3cbe27d6 --- /dev/null +++ b/src/test/java/io/github/dsheirer/dsp/c4fm/C4FMSymbolProcessorScoreTest.java @@ -0,0 +1,50 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2026 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 + * **************************************************************************** + */ + +package io.github.dsheirer.dsp.c4fm; + +import io.github.dsheirer.module.decode.p25.phase1.P25P1SymbolProcessorC4FM; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * JUnit tests for the sync correlation score in the C4FM symbol processor. + */ +public class C4FMSymbolProcessorScoreTest +{ + /** + * 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 starts just before the start of the buffer. + */ + @Test + public void scoreBeforeBufferStart() + { + float samplesPerSymbol = 25000f / 4800f; + P25P1SymbolProcessorC4FM processor = new P25P1SymbolProcessorC4FM(null, null); + processor.setSamplesPerSymbol(samplesPerSymbol); + //Sync workspace, sized as in setSamplesPerSymbol(), plus a 1024 sample buffer + int syncWorkspaceLength = (int)Math.ceil((processor.mSyncDetector.getDibitLength() + 1) * samplesPerSymbol); + processor.mBuffer = new float[syncWorkspaceLength + 1024]; + + //Offset to the final sync symbol that puts the first sync symbol at buffer index -0.3 + double offset = (processor.mSyncDetector.getDibitLength() - 1) * samplesPerSymbol - 0.3; + assertDoesNotThrow(() -> processor.score(offset, samplesPerSymbol)); + } +}