2023-07-18 20:59:32 +02:00
|
|
|
#include "deint.h"
|
|
|
|
|
#include <cstring>
|
2023-07-18 21:19:16 +02:00
|
|
|
#include <vector>
|
2023-07-18 20:59:32 +02:00
|
|
|
|
|
|
|
|
#define LEN(x) (sizeof(x) / sizeof(*x))
|
|
|
|
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
|
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
|
|
|
|
|
|
|
|
|
namespace meteor
|
|
|
|
|
{
|
|
|
|
|
const uint8_t _syncwords[] = {0x27, 0x4E, 0xD8, 0xB1};
|
|
|
|
|
|
|
|
|
|
inline int count_ones(uint64_t v)
|
|
|
|
|
{
|
|
|
|
|
int count;
|
|
|
|
|
for (count = 0; v; count++)
|
|
|
|
|
v &= v - 1;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int DeinterleaverReader::autocorrelate(phase_t *rotation, int period, uint8_t *hard, int len)
|
|
|
|
|
{
|
|
|
|
|
int i, j, k;
|
|
|
|
|
uint8_t tmp, _xor, window;
|
2023-08-21 14:45:44 +07:00
|
|
|
std::vector<int> ones_count(8 * period, 0);
|
|
|
|
|
std::vector<int> average_bit(8 * period + 8, 0);
|
2023-07-18 20:59:32 +02:00
|
|
|
int corr, best_corr, best_idx;
|
|
|
|
|
|
|
|
|
|
/* Make len a multiple of the period */
|
|
|
|
|
len -= len % period;
|
|
|
|
|
|
|
|
|
|
/* XOR the bitstream with a delayed version of itself */
|
|
|
|
|
for (i = 0; i < period; i++)
|
|
|
|
|
{
|
|
|
|
|
j = len - period + i - 1;
|
|
|
|
|
tmp = hard[j];
|
|
|
|
|
for (j -= period; j >= 0; j -= period)
|
|
|
|
|
{
|
|
|
|
|
_xor = hard[j] ^ tmp;
|
|
|
|
|
tmp = hard[j];
|
|
|
|
|
hard[j] = _xor;
|
|
|
|
|
|
|
|
|
|
/* Keep track of the average value of each bit in the period window */
|
|
|
|
|
for (k = 0; k < 8; k++)
|
|
|
|
|
{
|
|
|
|
|
average_bit[8 * i + 7 - k] += tmp & (1 << k) ? 1 : -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find the bit offset with the most zeroes */
|
|
|
|
|
window = 0;
|
|
|
|
|
hard--;
|
|
|
|
|
for (i = 0; i < 8 * (len - period); i++)
|
|
|
|
|
{
|
|
|
|
|
if (!(i % 8))
|
|
|
|
|
hard++;
|
|
|
|
|
window = (window >> 1) | ((*hard << (i % 8)) & 0x80);
|
|
|
|
|
ones_count[i % (8 * period)] += count_ones(window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
best_idx = 0;
|
|
|
|
|
best_corr = ones_count[0] - len / 64; /* Give offset 0 a small boost */
|
|
|
|
|
for (i = 1; i < (int)ones_count.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (ones_count[i] < best_corr)
|
|
|
|
|
{
|
|
|
|
|
best_corr = ones_count[i];
|
|
|
|
|
best_idx = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Collect the average syncword bits */
|
|
|
|
|
tmp = 0;
|
|
|
|
|
for (i = 7; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
tmp |= (average_bit[best_idx + i] > 0 ? 1 << i : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find the phase rotation of the syncword */
|
|
|
|
|
*rotation = (phase_t)0;
|
|
|
|
|
best_corr = count_ones(tmp ^ _syncwords[0]);
|
|
|
|
|
for (i = 1; i < (int)LEN(_syncwords); i++)
|
|
|
|
|
{
|
|
|
|
|
corr = count_ones(tmp ^ _syncwords[i]);
|
|
|
|
|
if (best_corr > corr)
|
|
|
|
|
{
|
|
|
|
|
best_corr = corr;
|
|
|
|
|
*rotation = (phase_t)i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return best_idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeinterleaverReader::deinterleave(int8_t *dst, const int8_t *src, size_t len)
|
|
|
|
|
{
|
|
|
|
|
int delay, write_idx, read_idx;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
read_idx = (_offset + INTER_BRANCH_COUNT * INTER_BRANCH_DELAY) % sizeof(_deint);
|
|
|
|
|
// assert(len < sizeof(_deint));
|
|
|
|
|
|
|
|
|
|
/* Write bits to the deinterleaver */
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
{
|
|
|
|
|
/* Skip sync marker */
|
|
|
|
|
if (!_cur_branch)
|
|
|
|
|
src += 8;
|
|
|
|
|
|
|
|
|
|
/* Compute the delay of the current symbol based on the branch we're on */
|
|
|
|
|
delay = (_cur_branch % INTER_BRANCH_COUNT) * INTER_BRANCH_DELAY * INTER_BRANCH_COUNT;
|
|
|
|
|
write_idx = (_offset - delay + sizeof(_deint)) % sizeof(_deint);
|
|
|
|
|
|
|
|
|
|
_deint[write_idx] = *src++;
|
|
|
|
|
|
|
|
|
|
_offset = (_offset + 1) % sizeof(_deint);
|
|
|
|
|
_cur_branch = (_cur_branch + 1) % INTER_MARKER_INTERSAMPS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read bits from the deinterleaver */
|
|
|
|
|
for (; len > 0; len--)
|
|
|
|
|
{
|
|
|
|
|
*dst++ = _deint[read_idx];
|
|
|
|
|
read_idx = (read_idx + 1) % sizeof(_deint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t DeinterleaverReader::deinterleave_num_samples(size_t output_count)
|
|
|
|
|
{
|
|
|
|
|
int num_syncs;
|
|
|
|
|
|
|
|
|
|
if (!output_count)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
num_syncs = (_cur_branch ? 0 : 1) + (output_count - (INTER_MARKER_INTERSAMPS - _cur_branch) + INTER_MARKER_INTERSAMPS - 1) / INTER_MARKER_INTERSAMPS;
|
|
|
|
|
|
|
|
|
|
return output_count + 8 * num_syncs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int DeinterleaverReader::deinterleave_expected_sync_offset()
|
|
|
|
|
{
|
|
|
|
|
return _cur_branch ? INTER_MARKER_INTERSAMPS - _cur_branch : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void soft_to_hard(uint8_t *hard, int8_t *soft, int len)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
// assert(!(len & 0x7));
|
|
|
|
|
|
|
|
|
|
while (len > 0)
|
|
|
|
|
{
|
|
|
|
|
*hard = 0;
|
|
|
|
|
|
|
|
|
|
for (i = 7; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
*hard |= (*soft < 0) << i;
|
|
|
|
|
soft++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hard++;
|
|
|
|
|
len -= 8;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeinterleaverReader::DeinterleaverReader()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeinterleaverReader::~DeinterleaverReader()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int DeinterleaverReader::read_samples(std::function<int(int8_t *, size_t)> read, int8_t *dst, size_t len)
|
|
|
|
|
{
|
|
|
|
|
uint8_t *hard = new uint8_t[INTER_SIZE(len)];
|
|
|
|
|
|
|
|
|
|
/* Retrieve enough samples so that the deinterleaver will output
|
|
|
|
|
* $len samples. Use the internal cache first */
|
|
|
|
|
int num_samples = deinterleave_num_samples(len);
|
|
|
|
|
if (offset)
|
|
|
|
|
{
|
|
|
|
|
memcpy(dst, from_prev, MIN(offset, num_samples));
|
|
|
|
|
memcpy(from_prev, from_prev + offset, offset - MIN(offset, num_samples));
|
|
|
|
|
}
|
|
|
|
|
if (num_samples - offset > 0 && !read(dst + offset, num_samples - offset))
|
|
|
|
|
{
|
|
|
|
|
delete[] hard;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
offset -= MIN(offset, num_samples);
|
|
|
|
|
|
|
|
|
|
if (num_samples < INTER_MARKER_STRIDE * 8)
|
|
|
|
|
{
|
|
|
|
|
/* Not enough bytes to reliably find sync marker offset: assume the
|
|
|
|
|
* offset is correct, and just derotate and deinterleave what we read */
|
|
|
|
|
// soft_derotate(dst, num_samples, rotation);
|
|
|
|
|
rotate_soft(dst, num_samples, rotation, false);
|
|
|
|
|
deinterleave(dst, dst, len);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Find synchronization marker (offset with the best autocorrelation) */
|
|
|
|
|
soft_to_hard(hard, dst, num_samples & ~0x7);
|
|
|
|
|
offset = autocorrelate(&rotation, INTER_MARKER_STRIDE / 8, hard, num_samples / 8);
|
|
|
|
|
|
|
|
|
|
/* Get where the deinterleaver expects the next marker to be */
|
|
|
|
|
int deint_offset = deinterleave_expected_sync_offset();
|
|
|
|
|
|
|
|
|
|
/* Compute the delta between the expected marker position and the
|
|
|
|
|
* one found by the correlator */
|
|
|
|
|
offset = (offset - deint_offset + INTER_MARKER_INTERSAMPS + 1) % INTER_MARKER_STRIDE;
|
|
|
|
|
offset = offset > INTER_MARKER_STRIDE / 2 ? offset - INTER_MARKER_STRIDE : offset;
|
|
|
|
|
|
|
|
|
|
/* If the offset is positive, read more
|
|
|
|
|
* bits to get $num_samples valid samples. If the offset is negative,
|
|
|
|
|
* copy the last few bytes into the local cache */
|
|
|
|
|
if (offset > 0)
|
|
|
|
|
{
|
|
|
|
|
if (!read(dst + num_samples, offset))
|
|
|
|
|
{
|
|
|
|
|
delete[] hard;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memcpy(from_prev, dst + num_samples + offset, -offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Correct rotation for these samples */
|
|
|
|
|
// soft_derotate(dst, num_samples + offset, rotation);
|
|
|
|
|
rotate_soft(dst, num_samples + offset, rotation, false);
|
|
|
|
|
|
|
|
|
|
/* Deinterleave */
|
|
|
|
|
deinterleave(dst, dst + offset, len);
|
|
|
|
|
offset = offset < 0 ? -offset : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete[] hard;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|