rtl_433/src/am_analyze.c

325 lines
12 KiB
C

/** @file
AM signal analyzer.
Copyright (C) 2018 Christian Zuckschwerdt
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 2 of the License, or
(at your option) any later version.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bitbuffer.h"
#include "samp_grab.h"
#include "fatal.h"
#include "am_analyze.h"
#define FRAME_END_MIN 50000 /* minimum sample count to detect frame end */
#define FRAME_PAD 10000 /* number of samples to pad both frame start and end */
am_analyze_t *am_analyze_create(void)
{
am_analyze_t *a;
a = calloc(1, sizeof(am_analyze_t));
if (!a)
WARN_CALLOC("am_analyze_create()");
return a; // NOTE: returns NULL on alloc failure.
}
void am_analyze_free(am_analyze_t *a)
{
free(a);
}
void am_analyze_skip(am_analyze_t *a, unsigned n_samples)
{
a->counter += n_samples;
a->signal_start = 0;
}
void am_analyze(am_analyze_t *a, int16_t *am_buf, unsigned n_samples, int debug_output, samp_grab_t *g)
{
unsigned int i;
int threshold = (a->level_limit ? a->level_limit : 8000); // Does not support auto level. Use old default instead.
for (i = 0; i < n_samples; i++) {
if (am_buf[i] > threshold) {
if (!a->signal_start)
a->signal_start = a->counter;
if (a->print) {
a->pulses_found++;
a->pulse_start = a->counter;
a->signal_pulse_data[a->signal_pulse_counter][0] = a->counter;
a->signal_pulse_data[a->signal_pulse_counter][1] = -1;
a->signal_pulse_data[a->signal_pulse_counter][2] = -1;
if (debug_output) {
fprintf(stderr, "pulse_distance %u\n", a->counter - a->pulse_end);
fprintf(stderr, "pulse_start distance %u\n", a->pulse_start - a->prev_pulse_start);
fprintf(stderr, "pulse_start[%u] found at sample %u, value = %d\n", a->pulses_found, a->counter, am_buf[i]);
}
a->prev_pulse_start = a->pulse_start;
a->print = 0;
a->print2 = 1;
}
}
a->counter++;
if (am_buf[i] < threshold) {
if (a->print2) {
a->pulse_avg += a->counter - a->pulse_start;
if (debug_output) {
fprintf(stderr, "pulse_end [%u] found at sample %u, pulse length = %u, pulse avg length = %u\n",
a->pulses_found, a->counter, a->counter - a->pulse_start, (a->pulses_found) ? (a->pulse_avg / a->pulses_found) : 0);
}
a->pulse_end = a->counter;
a->print2 = 0;
a->signal_pulse_data[a->signal_pulse_counter][1] = a->counter;
a->signal_pulse_data[a->signal_pulse_counter][2] = a->counter - a->pulse_start;
a->signal_pulse_counter++;
if (a->signal_pulse_counter >= PULSE_DATA_SIZE) {
a->signal_pulse_counter = 0;
fprintf(stderr, "Too many pulses detected, probably bad input data or input parameters\n");
return;
}
}
a->print = 1;
if (a->signal_start && (a->pulse_end + FRAME_END_MIN < a->counter)) {
unsigned padded_start = a->signal_start - FRAME_PAD;
unsigned padded_end = a->counter - FRAME_END_MIN + FRAME_PAD;
unsigned padded_len = padded_end - padded_start;
fprintf(stderr, "*** signal_start = %u, signal_end = %u, signal_len = %u, pulses_found = %u\n",
padded_start, padded_end, padded_len, a->pulses_found);
am_analyze_classify(a); // clears signal_pulse_data
a->pulses_found = 0;
if (g) {
samp_grab_write(g, padded_len, n_samples - i - 1);
}
a->signal_start = 0;
}
}
}
}
void am_analyze_classify(am_analyze_t *aa)
{
unsigned int i, k, max = 0, min = 1000000, t;
unsigned int delta, p_limit;
unsigned int a[3], b[2], a_cnt[3], a_new[3];
unsigned int signal_distance_data[PULSE_DATA_SIZE] = {0};
bitbuffer_t bits = {0};
unsigned int signal_type;
if (!aa->signal_pulse_data[0][0])
return;
for (i = 0; i < aa->signal_pulse_counter; i++) {
if (aa->signal_pulse_data[i][0] > 0) {
//fprintf(stderr, "[%03d] s: %d\t e:\t %d\t l:%d\n",
//i, aa->signal_pulse_data[i][0], aa->signal_pulse_data[i][1],
//aa->signal_pulse_data[i][2]);
if (aa->signal_pulse_data[i][2] > max)
max = aa->signal_pulse_data[i][2];
if (aa->signal_pulse_data[i][2] <= min)
min = aa->signal_pulse_data[i][2];
}
}
t = (max + min) / 2;
//fprintf(stderr, "\n\nMax: %d, Min: %d t:%d\n", max, min, t);
delta = (max - min)*(max - min);
//TODO use Lloyd-Max quantizer instead
k = 1;
while ((k < 10) && (delta > 0)) {
unsigned min_new = 0;
unsigned count_min = 0;
unsigned max_new = 0;
unsigned count_max = 0;
for (i = 0; i < aa->signal_pulse_counter; i++) {
if (aa->signal_pulse_data[i][0] > 0) {
if (aa->signal_pulse_data[i][2] < t) {
min_new = min_new + aa->signal_pulse_data[i][2];
count_min++;
} else {
max_new = max_new + aa->signal_pulse_data[i][2];
count_max++;
}
}
}
if (count_min != 0 && count_max != 0) {
min_new = min_new / count_min;
max_new = max_new / count_max;
}
delta = (min - min_new)*(min - min_new) + (max - max_new)*(max - max_new);
min = min_new;
max = max_new;
t = (min + max) / 2;
fprintf(stderr, "Iteration %u. t: %u min: %u (%u) max: %u (%u) delta %u\n", k, t, min, count_min, max, count_max, delta);
k++;
}
for (i = 0; i < aa->signal_pulse_counter; i++) {
if (aa->signal_pulse_data[i][0] > 0) {
//fprintf(stderr, "%d\n", aa->signal_pulse_data[i][1]);
}
}
/* 50% decision limit */
if (min != 0 && max / min > 1) {
fprintf(stderr, "Pulse coding: Short pulse length %u - Long pulse length %u\n", min, max);
signal_type = 2;
} else {
fprintf(stderr, "Distance coding: Pulse length %u\n", (min + max) / 2);
signal_type = 1;
}
p_limit = (max + min) / 2;
/* Initial guesses */
a[0] = 1000000;
a[2] = 0;
for (i = 1; i < aa->signal_pulse_counter; i++) {
if (aa->signal_pulse_data[i][0] > 0) {
// fprintf(stderr, "[%03d] s: %d\t e:\t %d\t l:%d\t d:%d\n",
// i, aa->signal_pulse_data[i][0], aa->signal_pulse_data[i][1],
// aa->signal_pulse_data[i][2], aa->signal_pulse_data[i][0]-aa->signal_pulse_data[i-1][1]);
signal_distance_data[i - 1] = aa->signal_pulse_data[i][0] - aa->signal_pulse_data[i - 1][1];
if (signal_distance_data[i - 1] > a[2])
a[2] = signal_distance_data[i - 1];
if (signal_distance_data[i - 1] <= a[0])
a[0] = signal_distance_data[i - 1];
}
}
min = a[0];
max = a[2];
a[1] = (a[0] + a[2]) / 2;
// for (i=0 ; i<1 ; i++) {
// b[i] = (a[i]+a[i+1])/2;
// }
b[0] = (a[0] + a[1]) / 2;
b[1] = (a[1] + a[2]) / 2;
// fprintf(stderr, "a[0]: %d\t a[1]: %d\t a[2]: %d\t\n",a[0],a[1],a[2]);
// fprintf(stderr, "b[0]: %d\t b[1]: %d\n",b[0],b[1]);
k = 1;
delta = 10000000;
while ((k < 10) && (delta > 0)) {
for (i = 0; i < 3; i++) {
a_new[i] = 0;
a_cnt[i] = 0;
}
for (i = 0; i < aa->signal_pulse_counter; i++) {
if (signal_distance_data[i] > 0) {
if (signal_distance_data[i] < b[0]) {
a_new[0] += signal_distance_data[i];
a_cnt[0]++;
} else if (signal_distance_data[i] < b[1] && signal_distance_data[i] >= b[0]) {
a_new[1] += signal_distance_data[i];
a_cnt[1]++;
} else if (signal_distance_data[i] >= b[1]) {
a_new[2] += signal_distance_data[i];
a_cnt[2]++;
}
}
}
// fprintf(stderr, "Iteration %d.", k);
delta = 0;
for (i = 0; i < 3; i++) {
if (a_cnt[i])
a_new[i] /= a_cnt[i];
delta += (a[i] - a_new[i])*(a[i] - a_new[i]);
// fprintf(stderr, "\ta[%d]: %d (%d)", i, a_new[i], a[i]);
a[i] = a_new[i];
}
// fprintf(stderr, " delta %d\n", delta);
if (a[0] < min) {
a[0] = min;
// fprintf(stderr, "Fixing a[0] = %d\n", min);
}
if (a[2] > max) {
a[0] = max;
// fprintf(stderr, "Fixing a[2] = %d\n", max);
}
// if (a[1] == 0) {
// a[1] = (a[2]+a[0])/2;
// fprintf(stderr, "Fixing a[1] = %d\n", a[1]);
// }
// fprintf(stderr, "Iteration %d.", k);
for (i = 0; i < 2; i++) {
// fprintf(stderr, "\tb[%d]: (%d) ", i, b[i]);
b[i] = (a[i] + a[i + 1]) / 2;
// fprintf(stderr, "%d ", b[i]);
}
// fprintf(stderr, "\n");
k++;
}
if (aa->override_short) {
p_limit = aa->override_short;
a[0] = aa->override_short;
}
if (aa->override_long) {
a[1] = aa->override_long;
}
fprintf(stderr, "\nShort distance: %u, long distance: %u, packet distance: %u\n", a[0], a[1], a[2]);
fprintf(stderr, "\np_limit: %u\n", p_limit);
bitbuffer_clear(&bits);
if (signal_type == 1) {
for (i = 0; i < aa->signal_pulse_counter; i++) {
if (signal_distance_data[i] > 0) {
if (signal_distance_data[i] < (a[0] + a[1]) / 2) {
// fprintf(stderr, "0 [%d] %d < %d\n",i, signal_distance_data[i], (a[0]+a[1])/2);
bitbuffer_add_bit(&bits, 0);
} else if ((signal_distance_data[i] > (a[0] + a[1]) / 2) && (signal_distance_data[i] < (a[1] + a[2]) / 2)) {
// fprintf(stderr, "0 [%d] %d > %d\n",i, signal_distance_data[i], (a[0]+a[1])/2);
bitbuffer_add_bit(&bits, 1);
} else if (signal_distance_data[i] > (a[1] + a[2]) / 2) {
// fprintf(stderr, "0 [%d] %d > %d\n",i, signal_distance_data[i], (a[1]+a[2])/2);
bitbuffer_add_row(&bits);
}
}
}
bitbuffer_print(&bits);
}
if (signal_type == 2) {
for (i = 0; i < aa->signal_pulse_counter; i++) {
if (aa->signal_pulse_data[i][2] > 0) {
if (aa->signal_pulse_data[i][2] < p_limit) {
// fprintf(stderr, "0 [%d] %d < %d\n",i, aa->signal_pulse_data[i][2], p_limit);
bitbuffer_add_bit(&bits, 0);
} else {
// fprintf(stderr, "1 [%d] %d > %d\n",i, aa->signal_pulse_data[i][2], p_limit);
bitbuffer_add_bit(&bits, 1);
}
if ((signal_distance_data[i] >= (a[1] + a[2]) / 2)) {
// fprintf(stderr, "\\n [%d] %d > %d\n",i, signal_distance_data[i], (a[1]+a[2])/2);
bitbuffer_add_row(&bits);
}
}
}
bitbuffer_print(&bits);
}
// clear signal_pulse_data
aa->signal_pulse_counter = 0;
}