Files
sdrtrunk/src/test/java/io/github/dsheirer/buffer/SharedNativeBufferTest.java
Denny Sheirer 85bfd21257 2537 Complex Vector Oscillator (256/512) Lanes Can Drift Apart Over Time (#2538)
* #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>
2026-10-30 08:42:49 -04:00

121 lines
4.1 KiB
Java

/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.buffer;
import io.github.dsheirer.sample.complex.ComplexSamples;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
/**
* Verifies that channels sharing a native buffer receive the same converted samples.
*/
public class SharedNativeBufferTest
{
private static byte[] samples()
{
byte[] samples = new byte[8192 * 8];
new Random(1).nextBytes(samples);
return samples;
}
private static List<ComplexSamples> read(INativeBuffer buffer)
{
List<ComplexSamples> fragments = new ArrayList<>();
Iterator<ComplexSamples> iterator = buffer.iterator();
while(iterator.hasNext())
{
fragments.add(iterator.next());
}
return fragments;
}
/**
* Test: the samples match the wrapped buffer and each consumer gets the same instances.
*/
@Test
void consumersShareOneConversion()
{
ByteNativeBuffer nativeBuffer = new ByteNativeBuffer(samples(), 1000L, 0.0f, 2400.0f);
SharedNativeBuffer shared = new SharedNativeBuffer(nativeBuffer);
List<ComplexSamples> expected = read(nativeBuffer);
List<ComplexSamples> first = read(shared);
List<ComplexSamples> second = read(shared);
assertEquals(expected.size(), first.size());
assertEquals(expected.size(), second.size());
for(int x = 0; x < expected.size(); x++)
{
assertArrayEquals(expected.get(x).i(), first.get(x).i());
assertArrayEquals(expected.get(x).q(), first.get(x).q());
assertEquals(expected.get(x).timestamp(), first.get(x).timestamp());
assertSame(first.get(x), second.get(x));
}
}
/**
* Test: concurrent consumers each receive every fragment in order.
*/
@Test
void concurrentConsumersReceiveAllFragments() throws Exception
{
ByteNativeBuffer nativeBuffer = new ByteNativeBuffer(samples(), 1000L, 0.0f, 2400.0f);
int fragmentCount = read(nativeBuffer).size();
ExecutorService executor = Executors.newFixedThreadPool(4);
try
{
for(int trial = 0; trial < 200; trial++)
{
SharedNativeBuffer shared = new SharedNativeBuffer(nativeBuffer);
List<Future<List<ComplexSamples>>> results = new ArrayList<>();
for(int consumer = 0; consumer < 4; consumer++)
{
results.add(executor.submit(() -> read(shared)));
}
List<ComplexSamples> first = results.getFirst().get();
assertEquals(fragmentCount, first.size());
for(Future<List<ComplexSamples>> result : results)
{
assertEquals(first, result.get());
}
}
}
finally
{
executor.shutdownNow();
}
}
}