- run length algorithm for picking datagrams out of the bit stream implemented.

- L(ength) field fixed for C1 Mode B datagrams.
- Allow to redefine CFLAGS and OUTPUT directory.
pull/18/head
Xael South 2021-01-26 11:50:05 +01:00
rodzic f213753a98
commit 355ba64a57
4 zmienionych plików z 406 dodań i 240 usunięć

Wyświetl plik

@ -55,19 +55,41 @@ Before building Android version the SDK and NDK have to be installed. See androi
Carrier-frequency given at "-f" must be set properly. With my DVB-T-Receiver I had to choose carrier 50kHz under the standard of 868.95MHz. Sample rate at 1.6Ms/s is hardcoded and cannot be changed.
samples2.bin is a "live" example with two devices received.
samples/samples2.bin is a live example with two devices received.
On Android first the driver must be started with options given above. IQ-data goes to a port which is would be already set by driver settings. Use get_net to get IQ-data into rtl_wmbus.
Bugfixing
-----
Mode C1 datagram type B is supported now - thanks to Fredrik Öhrström for spotting this bug and for providing raw datagram samples.
An another thanks to Kjell Braden (afflux) and to carlos62 for the idea how to fix this.
Mode C1 datagram type B is supported now - thanks to Fredrik Öhrström for spotting this and for providing raw datagram samples.
An another thanks goes to Kjell Braden (afflux) and to carlos62 for the idea how to implement this.
Redefining CFLAGS and OUTPUT directory is allowed now (patch sent by dwrobel).
L(ength) field from C1 mode B datagrams does not include CRC bytes anymore: L field will now be printed as if the datagram
would be received from a T1 or C1 mode A meter.
Improvements
-----
A new method for picking datagrams out of the bit stream that _could_ probably better perform in C1 mode has been implemented.
I called that "run length algorithm". I don't know if any similar term already exists in the literature.
The new method is very sensitive to the bit glitches, which have to be filtered out of the bit stream with an asymmetrical glitch filter.
The glitch filter _must_ be implemented asymmetrical in this case, because RTL-SDR produces more "0" bits than "1" bits on it's output.
The latter seems more to be a hardware feature rather than a bug.
Run length algorithm is running in parallel (and fully independantly) to the time2 method. You will eventually get two
identical datagrams - each of them decoded by one of the both methods. If you really want avoiding duplicates, then start
rtl_wmbus with "-r 0" or "-t 0" argument to prevent executing of run length or time2 method respectively.
You can play with arguments and check which method performs better for you. Please note, that both method are active by default.
An additional method introduces more calculation steps, so I'm not sure if Raspberry Pi 1 will still do.
Run length algorithm works well with a few mode C1 devices I had around me, but can still be improved with your help.
License
-------
Copyright (c) 2017 <xael.south@yandex.com>
Copyright (c) 2021 <xael.south@yandex.com>
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
are met:

Wyświetl plik

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2017 <xael.south@yandex.com>
* Copyright (c) 2021 <xael.south@yandex.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -23,6 +23,7 @@
* SUCH DAMAGE.
*/
#include <getopt.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
@ -38,11 +39,49 @@
#include "net_support.h"
#include "t1_c1_packet_decoder.h"
static const uint32_t ACCESS_CODE = 0b0101010101010000111101u;
static const uint32_t ACCESS_CODE_BITMASK = 0x3FFFFFu;
static const unsigned ACCESS_CODE_ERRORS = 1u; // 0 if no errors allowed
#if defined(__SSE4_2__)
#include <immintrin.h>
#endif
/* deglitch_filter has been calculated by Python script as follows.
The filter is counting "1" among 7 bits and saying "1" if count("1") >= 3 else "0".
Notice here count("1") >= 3. (More intuitive in that case would be count("1") >= 3.5.)
That forces the filter to put more "1" than "0" on the output, because RTL-SDR streams
more "0" than "1" - i don't know why RTL-SDR do this.
x = 'static const uint8_t deglitch_filter[128] = {'
mod8 = 8
for i in range(2**7):
s = '{0:07b};'.format(i)
val = '1' if bin(i).count("1") >= 3 else '0'
print(s[0] + ";" + s[1] + ";" + s[2] + ";" + s[3] + ";" + s[4] + ";" + s[5] + ";" + s[6] + ";;%d;;%s" % (bin(i).count("1"), val))
if i % 8 == 0: x += '\n\t'
x += val + ','
x += '};\n'
print(x)
*/
static const uint8_t deglitch_filter[128] =
{
0,0,0,0,0,0,0,1,
0,0,0,1,0,1,1,1,
0,0,0,1,0,1,1,1,
0,1,1,1,1,1,1,1,
0,0,0,1,0,1,1,1,
0,1,1,1,1,1,1,1,
0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
0,0,0,1,0,1,1,1,
0,1,1,1,1,1,1,1,
0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1
};
static float lp_1600kHz_56kHz(int sample, size_t i_or_q)
{
@ -284,46 +323,177 @@ static inline unsigned count_set_bits(uint32_t n)
}
static inline int majority_votes_bitfilter(uint32_t unfilt_bitstream, uint32_t bits_in_unfilt_bitstream)
struct runlength_algorithm
{
const unsigned ones = count_set_bits(unfilt_bitstream & bits_in_unfilt_bitstream);
const bool odd = (ones & 1) > 0;
const uint32_t bits_in_unfilt_bitstream_half = count_set_bits(bits_in_unfilt_bitstream)/2;
int run_length;
int bit_length;
int cum_run_length_error;
unsigned state;
uint32_t raw_bitstream;
uint32_t bitstream;
struct t1_c1_packet_decoder_work decoder;
};
if (odd)
return (ones <= bits_in_unfilt_bitstream_half) ? 0 : 1;
if (ones < bits_in_unfilt_bitstream_half)
return 0;
if (ones > bits_in_unfilt_bitstream_half)
return 1;
return unfilt_bitstream & 1;
static void runlength_algorithm_reset(struct runlength_algorithm *algo)
{
algo->run_length = 0;
algo->bit_length = 8 * 256;
algo->cum_run_length_error = 0;
algo->state = 0u;
algo->raw_bitstream = 0;
algo->bitstream = 0;
reset_t1_c1_packet_decoder(&algo->decoder);
}
typedef void (*OutFunction)(unsigned bit, unsigned rssi);
static inline void to_stdout(unsigned bit, unsigned rssi)
static void runlength_algorithm(unsigned raw_bit, unsigned rssi, struct runlength_algorithm *algo)
{
(void)rssi;
algo->raw_bitstream = (algo->raw_bitstream << 1) | raw_bit;
const uint8_t tmp = bit;
const unsigned state = deglitch_filter[algo->raw_bitstream & 0x3Fu];
fwrite(&tmp, sizeof(tmp), 1, stdout);
if (algo->state == state)
{
algo->run_length++;
}
else
{
if (algo->run_length < 5)
{
runlength_algorithm_reset(algo);
algo->state = state;
algo->run_length = 1;
return;
}
const int unscaled_run_length = algo->run_length;
algo->run_length *= 256; // resolution scaling up for fixed point calculation
const int half_bit_length = algo->bit_length / 2;
if (algo->run_length <= half_bit_length)
{
runlength_algorithm_reset(algo);
algo->state = state;
algo->run_length = 1;
return;
}
int num_of_bits_rx;
for (num_of_bits_rx = 0; algo->run_length > half_bit_length; num_of_bits_rx++)
{
algo->run_length -= algo->bit_length;
unsigned bit = algo->state;
algo->bitstream = (algo->bitstream << 1) | bit;
if (count_set_bits((algo->bitstream & ACCESS_CODE_BITMASK) ^ ACCESS_CODE) <= ACCESS_CODE_ERRORS)
{
bit |= (1u<<PACKET_PREAMBLE_DETECTED_SHIFT); // packet detected; mark the bit similar to "Access Code"-Block in GNU Radio
}
t1_c1_packet_decoder(bit, rssi, &algo->decoder, "rla;");
}
#if 0
const int bit_error_length = algo->run_length / num_of_bits_rx;
if (in_rx_t1_c1_packet_decoder(&algo->decoder))
{
fprintf(stdout, "rl = %d, num_of_bits_rx = %d, bit_length = %d, old_bit_error_length = %d, new_bit_error_length = %d\n",
unscaled_run_length, num_of_bits_rx, algo->bit_length, algo->bit_error_length, bit_error_length);
}
#endif
// Some kind of PI controller is implemented below: u[n] = u[n-1] + Kp * e[n] + Ki * sum(e[0..n]).
// Kp and Ki were found by experiment; e[n] := algo->run_length
algo->cum_run_length_error += algo->run_length; // sum(e[0..n])
#define PI_KP 32
#define PI_KI 16
//algo->bit_length += (algo->run_length / PI_KP + algo->cum_run_length_error / PI_KI) / num_of_bits_rx;
algo->bit_length += (algo->run_length + algo->cum_run_length_error / PI_KI) / (PI_KP * num_of_bits_rx);
#undef PI_KI
#undef PI_KP
algo->state = state;
algo->run_length = 1;
}
}
struct time2_algorithm
{
uint32_t bitstream;
struct t1_c1_packet_decoder_work decoder;
};
static const OutFunction out_functions[] = { to_stdout, t1_c1_packet_decoder };
static void time2_algorithm_reset(struct time2_algorithm *algo)
{
algo->bitstream = 0;
reset_t1_c1_packet_decoder(&algo->decoder);
}
static void time2_algorithm(unsigned bit, unsigned rssi, struct time2_algorithm *algo)
{
algo->bitstream = (algo->bitstream << 1) | bit;
if (count_set_bits((algo->bitstream & ACCESS_CODE_BITMASK) ^ ACCESS_CODE) <= ACCESS_CODE_ERRORS)
{
bit |= (1u<<PACKET_PREAMBLE_DETECTED_SHIFT); // packet detected; mark the bit similar to "Access Code"-Block in GNU Radio
}
t1_c1_packet_decoder(bit, rssi, &algo->decoder, "t2a;");
}
static int opts_run_length_algorithm_enabled = 1;
static int opts_time2_algorithm_enabled = 1;
static void print_usage(const char *program_name)
{
fprintf(stdout, "Usage %s:\n", program_name);
fprintf(stdout, "\t-r 0 to disable run length algorithm\n");
fprintf(stdout, "\t-t 0 to disable time2 algorithm\n");
}
static void process_options(int argc, char *argv[])
{
int option;
while ((option = getopt(argc, argv, "r:t:")) != -1)
{
switch (option)
{
case 'r':
if (strcmp(optarg, "0") == 0)
{
opts_run_length_algorithm_enabled = 0;
}
else
{
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
break;
case 't':
if (strcmp(optarg, "0") == 0)
{
opts_time2_algorithm_enabled = 0;
}
else
{
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
break;
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
}
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
process_options(argc, argv);
// --- parameter section begin ---
// The idea behind the variables in the section is to make parameters
@ -331,37 +501,26 @@ int main(int argc, char *argv[])
const unsigned CLOCK_LOCK_THRESHOLD = 2;
const unsigned DECIMATION_RATE = 2;
//#define USING_BITFILTER
const uint32_t ACCESS_CODE = 0b0101010101010000111101u;
const uint32_t ACCESS_CODE_BITMASK = 0x3FFFFFu;
const unsigned ACCESS_CODE_ERRORS = 1u; // 0 if no errors allowed
// --- parameter section end ---
// Select function for output
OutFunction out_function = out_functions[1];
__attribute__((__aligned__(16))) uint8_t samples[4096];
float i = 0, q = 0;
unsigned decimation_rate_index = 0;
int16_t old_clock = INT16_MIN;
uint32_t bitstream = 0;
unsigned clock_lock = 0;
#if defined(USING_BITFILTER)
uint32_t unfilt_bitstream = 0;
uint32_t bits_in_unfilt_bitstream = 0;
#endif
struct time2_algorithm t2_algo;
time2_algorithm_reset(&t2_algo);
#if defined (__SSE4_2__)
__attribute__((__aligned__(16))) int16_t iq_samples[sizeof(samples)];
const __m128i dc_offset = _mm_set_epi16(-127, -127, -127, -127, -127, -127, -127, -127);
#endif
struct runlength_algorithm rl_algo;
runlength_algorithm_reset(&rl_algo);
//FILE *input = fopen("samples.bin", "rb");
//FILE *input = fopen("samples/samples2.bin", "rb");
//FILE *input = fopen("samples/kamstrup.bin", "rb");
//FILE *input = fopen("samples/c1_mode_b.bin", "rb");
//FILE *input = fopen("samples/t1_c1a_mixed.bin", "rb");
//FILE *input = get_net("localhost", 14423);
FILE *input= stdin;
FILE *input = stdin;
if (input == NULL)
{
@ -373,6 +532,7 @@ int main(int argc, char *argv[])
//FILE *demod_out2 = fopen("demod.bin", "wb");
//FILE *clock_out = fopen("clock.bin", "wb");
//FILE *bits_out= fopen("bits.bin", "wb");
//FILE *rawbits_out = fopen("rawbits.bin", "wb");
while (!feof(input))
{
@ -383,24 +543,10 @@ int main(int argc, char *argv[])
return 2;
}
#if defined (__SSE4_2__)
for (size_t k = 0; k < sizeof(samples)/sizeof(samples[0]); k += 8) // +2 : i and q interleaved
{
__m128i tmp = _mm_loadu_si128((__m128i const*)&samples[k]); // Hmmm, loading 8 byte besides of upper boundary?..
__m128i cvt = _mm_add_epi16(_mm_cvtepu8_epi16(tmp), dc_offset);
_mm_store_si128((__m128i *)&iq_samples[k], cvt);
}
#endif
for (size_t k = 0; k < sizeof(samples)/sizeof(samples[0]); k += 2) // +2 : i and q interleaved
{
#if defined (__SSE4_2__)
const int i_unfilt = iq_samples[k];
const int q_unfilt = iq_samples[k+1];
#else
const int i_unfilt = ((int)samples[k] - 127);
const int q_unfilt = ((int)samples[k + 1] - 127);
#endif
// Low-Pass-Filtering before decimation is necessary, to ensure
// that i and q signals don't contain frequencies above new sample
@ -436,7 +582,6 @@ int main(int argc, char *argv[])
// Demodulate.
float delta_phi = polar_discriminator(i, q);
//int16_t demodulated_signal = (INT16_MAX-1)*delta_phi;
//fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out);
@ -445,6 +590,26 @@ int main(int argc, char *argv[])
//int16_t demodulated_signal = (INT16_MAX-1)*delta_phi;
//fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out2);
// Get the bit!
unsigned bit = (delta_phi >= 0) ? (1u<<PACKET_DATABIT_SHIFT) : (0u<<PACKET_DATABIT_SHIFT);
//int16_t u = bit ? (INT16_MAX-1) : 0;
//fwrite(&u, sizeof(u), 1, rawbits_out);
// --- rssi filtering section begin ---
// We are using one simple filter to rssi value in order to
// prevent unexpected "splashes" in signal power.
float rssi = sqrtf(i*i + q*q);
rssi = rssi_filter(rssi); // comment out, if rssi filtering is unwanted
#if defined(USE_MOVING_AVERAGE)
// If using moving average, we would have doubles of each of i- and q- signal components.
rssi /= DECIMATION_RATE;
#endif
// --- rssi filtering section end ---
// --- runlength algorithm section begin ---
if (opts_run_length_algorithm_enabled) runlength_algorithm(bit, rssi, &rl_algo);
// --- runlength algorithm section end ---
// --- clock recovery section begin ---
// The time-2 method is implemented: push squared signal through a bandpass
// tuned close to the symbol rate. Saturating band-pass output produces a
@ -453,26 +618,9 @@ int main(int argc, char *argv[])
const int16_t clock = (bp_iir_cheb1_800kHz_90kHz_98kHz_102kHz_110kHz(delta_phi * delta_phi) >= 0) ? INT16_MAX : INT16_MIN;
//fwrite(&clock, sizeof(clock), 1, clock_out);
unsigned bit = (delta_phi >= 0) ? (1u<<PACKET_DATABIT_SHIFT) : (0u<<PACKET_DATABIT_SHIFT);
#if defined(USING_BITFILTER)
unfilt_bitstream = (unfilt_bitstream << 1) | bit;
bits_in_unfilt_bitstream = (bits_in_unfilt_bitstream << 1) | 1;
#endif
// We are using one simple filter to rssi value in order to
// prevent unexpected "splashes" in signal power.
float rssi = sqrtf(i*i + q*q);
rssi = rssi_filter(rssi); // comment out, if rssi filtering is unwanted
if (clock > old_clock) // rising edge
{
clock_lock = 1;
#if defined(USING_BITFILTER)
unfilt_bitstream = bit;
bits_in_unfilt_bitstream = 1;
#endif
}
else if (old_clock == clock && clock_lock < CLOCK_LOCK_THRESHOLD)
{
@ -482,25 +630,8 @@ int main(int argc, char *argv[])
{
clock_lock++;
#if defined(USE_MOVING_AVERAGE)
// If using moving average, we would habe doubles of each of i- and q- signal components.
rssi /= DECIMATION_RATE;
#endif
#if defined(USING_BITFILTER)
// Bitfilter can be used to remove unwanted spikes in the demodulated signal.
bit = majority_votes_bitfilter(unfilt_bitstream, bits_in_unfilt_bitstream);
#endif
bitstream = (bitstream << 1) | bit;
if (count_set_bits((bitstream & ACCESS_CODE_BITMASK) ^ ACCESS_CODE) <= ACCESS_CODE_ERRORS)
{
bit |= (1u<<PACKET_PREAMBLE_DETECTED_SHIFT); // packet detected; mark the bit similar to "Access Code"-Block in GNU Radio
}
//fwrite(&bit, sizeof(bit), 1, bits_out);
out_function(bit, rssi);
if (opts_time2_algorithm_enabled) time2_algorithm(bit, rssi, &t2_algo);
}
old_clock = clock;
// --- clock recovery section end ---

Wyświetl plik

@ -2,7 +2,7 @@
#define T1_C1_PACKET_DECODER_H
/*-
* Copyright (c) 2017 <xael.south@yandex.com>
* Copyright (c) 2021 <xael.south@yandex.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -134,34 +134,35 @@ static const uint16_t CRC16_DNP_TABLE[] =
};
typedef void (*t1_c1_packet_decoder_state)(unsigned bit);
struct t1_c1_packet_decoder_work;
typedef void (*t1_c1_packet_decoder_state)(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void idle(unsigned bit);
static void done(unsigned bit);
static void rx_bit(unsigned bit);
static void idle(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void done(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_high_nibble_first_lfield_bit(unsigned bit);
static void rx_high_nibble_last_lfield_bit(unsigned bit);
static void rx_high_nibble_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_high_nibble_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_low_nibble_first_lfield_bit(unsigned bit);
static void rx_low_nibble_last_lfield_bit(unsigned bit);
static void rx_low_nibble_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_low_nibble_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_high_nibble_first_data_bit(unsigned bit);
static void rx_high_nibble_last_data_bit(unsigned bit);
static void rx_high_nibble_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_high_nibble_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_low_nibble_first_data_bit(unsigned bit);
static void rx_low_nibble_last_data_bit(unsigned bit);
static void rx_low_nibble_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void rx_low_nibble_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void c1_rx_bit(unsigned bit);
static void c1_rx_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void c1_rx_first_mode_bit(unsigned bit);
static void c1_rx_last_mode_bit(unsigned bit);
static void c1_rx_first_mode_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void c1_rx_last_mode_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void c1_rx_first_lfield_bit(unsigned bit);
static void c1_rx_last_lfield_bit(unsigned bit);
static void c1_rx_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void c1_rx_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void c1_rx_first_data_bit(unsigned bit);
static void c1_rx_last_data_bit(unsigned bit);
static void c1_rx_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static void c1_rx_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder);
static const t1_c1_packet_decoder_state states[] =
@ -225,7 +226,7 @@ static const t1_c1_packet_decoder_state states[] =
};
static struct
struct t1_c1_packet_decoder_work
{
const t1_c1_packet_decoder_state *state;
unsigned current_rssi;
@ -247,7 +248,7 @@ static struct
unsigned byte;
uint8_t packet[290]; // max. packet length with L- and all CRC-Fields
char timestamp[64];
} t1_c1_packet_decoder_work = {.state = &states[0]}; // idle
};
int get_mode_a_tlg_length(uint8_t lfield)
{
@ -260,126 +261,131 @@ int get_mode_b_tlg_length(uint8_t lfield)
return 1 + (int)lfield;
}
static void reset_t1_c1_packet_decoder(void)
static int in_rx_t1_c1_packet_decoder(struct t1_c1_packet_decoder_work *decoder)
{
memset(&t1_c1_packet_decoder_work, 0, sizeof(t1_c1_packet_decoder_work));
t1_c1_packet_decoder_work.state = &states[0];
return (decoder->state == &states[0]) ? 0 : 1;
}
static void idle(unsigned bit)
static void reset_t1_c1_packet_decoder(struct t1_c1_packet_decoder_work *decoder)
{
memset(decoder, 0, sizeof(*decoder));
decoder->state = &states[0];
}
static void idle(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
if (!(bit & PACKET_PREAMBLE_DETECTED_MASK))
{
reset_t1_c1_packet_decoder();
reset_t1_c1_packet_decoder(decoder);
}
}
static void done(unsigned bit)
static void done(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
(void)bit;
}
static void rx_bit(unsigned bit)
static void rx_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
}
static void rx_high_nibble_first_lfield_bit(unsigned bit)
static void rx_high_nibble_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte = (bit & PACKET_DATABIT_MASK);
t1_c1_packet_decoder_work.packet_rssi = t1_c1_packet_decoder_work.current_rssi;
decoder->byte = (bit & PACKET_DATABIT_MASK);
decoder->packet_rssi = decoder->current_rssi;
}
static void rx_high_nibble_last_lfield_bit(unsigned bit)
static void rx_high_nibble_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
t1_c1_packet_decoder_work.mode = t1_c1_packet_decoder_work.byte;
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
decoder->mode = decoder->byte;
t1_c1_packet_decoder_work.L = HIGH_NIBBLE_3OUTOF6[t1_c1_packet_decoder_work.byte];
t1_c1_packet_decoder_work.flags = 0;
decoder->L = HIGH_NIBBLE_3OUTOF6[decoder->byte];
decoder->flags = 0;
}
static void rx_low_nibble_first_lfield_bit(unsigned bit)
static void rx_low_nibble_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte = (bit & PACKET_DATABIT_MASK);
decoder->byte = (bit & PACKET_DATABIT_MASK);
}
static void rx_low_nibble_last_lfield_bit(unsigned bit)
static void rx_low_nibble_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
t1_c1_packet_decoder_work.mode <<= 6;
t1_c1_packet_decoder_work.mode |= t1_c1_packet_decoder_work.byte;
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
decoder->mode <<= 6;
decoder->mode |= decoder->byte;
const unsigned byte = LOW_NIBBLE_3OUTOF6[t1_c1_packet_decoder_work.byte];
const unsigned byte = LOW_NIBBLE_3OUTOF6[decoder->byte];
if (t1_c1_packet_decoder_work.L == 0xFFu || byte == 0xFFu)
if (decoder->L == 0xFFu || byte == 0xFFu)
{
if (t1_c1_packet_decoder_work.mode == C1_MODE_A)
if (decoder->mode == C1_MODE_A)
{
t1_c1_packet_decoder_work.b_frame_type = 0;
t1_c1_packet_decoder_work.state = &states[26]; // c1_rx_first_mode_bit
decoder->b_frame_type = 0;
decoder->state = &states[26]; // c1_rx_first_mode_bit
}
else if (t1_c1_packet_decoder_work.mode == C1_MODE_B)
else if (decoder->mode == C1_MODE_B)
{
t1_c1_packet_decoder_work.b_frame_type = 1;
t1_c1_packet_decoder_work.state = &states[26]; // c1_rx_first_mode_bit
decoder->b_frame_type = 1;
decoder->state = &states[26]; // c1_rx_first_mode_bit
}
else
{
reset_t1_c1_packet_decoder();
reset_t1_c1_packet_decoder(decoder);
}
}
else
{
t1_c1_packet_decoder_work.b_frame_type = 0;
t1_c1_packet_decoder_work.c1_packet = 0;
decoder->b_frame_type = 0;
decoder->c1_packet = 0;
t1_c1_packet_decoder_work.L |= byte;
t1_c1_packet_decoder_work.l = 0;
t1_c1_packet_decoder_work.packet[t1_c1_packet_decoder_work.l++] = t1_c1_packet_decoder_work.L;
t1_c1_packet_decoder_work.L = FULL_TLG_LENGTH_FROM_L_FIELD[t1_c1_packet_decoder_work.L];
decoder->L |= byte;
decoder->l = 0;
decoder->packet[decoder->l++] = decoder->L;
decoder->L = FULL_TLG_LENGTH_FROM_L_FIELD[decoder->L];
}
}
static void rx_high_nibble_first_data_bit(unsigned bit)
static void rx_high_nibble_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte = (bit & PACKET_DATABIT_MASK);
decoder->byte = (bit & PACKET_DATABIT_MASK);
}
static void rx_high_nibble_last_data_bit(unsigned bit)
static void rx_high_nibble_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
const unsigned byte = HIGH_NIBBLE_3OUTOF6[t1_c1_packet_decoder_work.byte];
const unsigned byte = HIGH_NIBBLE_3OUTOF6[decoder->byte];
if (byte == 0xFFu) t1_c1_packet_decoder_work.err_3outof = 1;
if (byte == 0xFFu) decoder->err_3outof = 1;
t1_c1_packet_decoder_work.packet[t1_c1_packet_decoder_work.l] = byte;
decoder->packet[decoder->l] = byte;
}
static void rx_low_nibble_first_data_bit(unsigned bit)
static void rx_low_nibble_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte = (bit & PACKET_DATABIT_MASK);
decoder->byte = (bit & PACKET_DATABIT_MASK);
}
static void rx_low_nibble_last_data_bit(unsigned bit)
static void rx_low_nibble_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
const unsigned byte = LOW_NIBBLE_3OUTOF6[t1_c1_packet_decoder_work.byte];
const unsigned byte = LOW_NIBBLE_3OUTOF6[decoder->byte];
if (byte == 0xFFu) t1_c1_packet_decoder_work.err_3outof = 1;
if (byte == 0xFFu) decoder->err_3outof = 1;
t1_c1_packet_decoder_work.packet[t1_c1_packet_decoder_work.l++] |= byte;
decoder->packet[decoder->l++] |= byte;
if (t1_c1_packet_decoder_work.l < t1_c1_packet_decoder_work.L)
if (decoder->l < decoder->L)
{
t1_c1_packet_decoder_work.state = &states[13]; // rx_high_nibble_first_data_bit
decoder->state = &states[13]; // rx_high_nibble_first_data_bit
}
else
{
@ -387,77 +393,77 @@ static void rx_low_nibble_last_data_bit(unsigned bit)
time(&now);
struct tm *timeinfo = gmtime(&now);
strftime(t1_c1_packet_decoder_work.timestamp, sizeof(t1_c1_packet_decoder_work.timestamp), "%Y-%m-%d %H:%M:%S.000", timeinfo);
strftime(decoder->timestamp, sizeof(decoder->timestamp), "%Y-%m-%d %H:%M:%S.000", timeinfo);
}
}
static void c1_rx_bit(unsigned bit)
static void c1_rx_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
}
static void c1_rx_first_mode_bit(unsigned bit)
static void c1_rx_first_mode_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte = (bit & PACKET_DATABIT_MASK);
decoder->byte = (bit & PACKET_DATABIT_MASK);
}
static void c1_rx_last_mode_bit(unsigned bit)
static void c1_rx_last_mode_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
t1_c1_packet_decoder_work.mode <<= 4;
t1_c1_packet_decoder_work.mode |= t1_c1_packet_decoder_work.byte;
decoder->mode <<= 4;
decoder->mode |= decoder->byte;
if (t1_c1_packet_decoder_work.byte == C1_MODE_AB_TRAILER)
if (decoder->byte == C1_MODE_AB_TRAILER)
{
t1_c1_packet_decoder_work.c1_packet = 1;
decoder->c1_packet = 1;
}
else
{
reset_t1_c1_packet_decoder();
reset_t1_c1_packet_decoder(decoder);
}
}
static void c1_rx_first_lfield_bit(unsigned bit)
static void c1_rx_first_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte = (bit & PACKET_DATABIT_MASK);
decoder->byte = (bit & PACKET_DATABIT_MASK);
}
static void c1_rx_last_lfield_bit(unsigned bit)
static void c1_rx_last_lfield_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
t1_c1_packet_decoder_work.L = t1_c1_packet_decoder_work.byte;
t1_c1_packet_decoder_work.l = 0;
t1_c1_packet_decoder_work.packet[t1_c1_packet_decoder_work.l++] = t1_c1_packet_decoder_work.L;
if (t1_c1_packet_decoder_work.b_frame_type)
decoder->L = decoder->byte;
decoder->l = 0;
decoder->packet[decoder->l++] = decoder->L;
if (decoder->b_frame_type)
{
t1_c1_packet_decoder_work.L = get_mode_b_tlg_length(t1_c1_packet_decoder_work.L);
decoder->L = get_mode_b_tlg_length(decoder->L);
}
else
{
t1_c1_packet_decoder_work.L = FULL_TLG_LENGTH_FROM_L_FIELD[t1_c1_packet_decoder_work.L];
decoder->L = FULL_TLG_LENGTH_FROM_L_FIELD[decoder->L];
}
}
static void c1_rx_first_data_bit(unsigned bit)
static void c1_rx_first_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte = (bit & PACKET_DATABIT_MASK);
decoder->byte = (bit & PACKET_DATABIT_MASK);
}
static void c1_rx_last_data_bit(unsigned bit)
static void c1_rx_last_data_bit(unsigned bit, struct t1_c1_packet_decoder_work *decoder)
{
t1_c1_packet_decoder_work.byte <<= 1;
t1_c1_packet_decoder_work.byte |= (bit & PACKET_DATABIT_MASK);
decoder->byte <<= 1;
decoder->byte |= (bit & PACKET_DATABIT_MASK);
t1_c1_packet_decoder_work.packet[t1_c1_packet_decoder_work.l++] = t1_c1_packet_decoder_work.byte;
decoder->packet[decoder->l++] = decoder->byte;
if (t1_c1_packet_decoder_work.l < t1_c1_packet_decoder_work.L)
if (decoder->l < decoder->L)
{
t1_c1_packet_decoder_work.state = &states[38]; // c1_rx_first_data_bit
decoder->state = &states[38]; // c1_rx_first_data_bit
}
else
{
@ -465,7 +471,7 @@ static void c1_rx_last_data_bit(unsigned bit)
time(&now);
struct tm *timeinfo = gmtime(&now);
strftime(t1_c1_packet_decoder_work.timestamp, sizeof(t1_c1_packet_decoder_work.timestamp), "%Y-%m-%d %H:%M:%S.000", timeinfo);
strftime(decoder->timestamp, sizeof(decoder->timestamp), "%Y-%m-%d %H:%M:%S.000", timeinfo);
}
}
@ -590,6 +596,7 @@ static unsigned cook_pkt(uint8_t *data, unsigned datalen)
/** @brief Strip CRCs in place. */
static unsigned cook_pkt_b_frame_type(uint8_t *data, unsigned datalen)
{
uint8_t *const L = data;
uint8_t *dst = data;
unsigned dstlen = 0;
@ -620,6 +627,11 @@ static unsigned cook_pkt_b_frame_type(uint8_t *data, unsigned datalen)
datalen -= datalen;
}
}
// L field has to be a number of data bytes in the datagram without CRC bytes.
// dstlen is this "number of data bytes" already but has an add-on of L field itself,
// which is going to be subtracted in the next step.
*L = dstlen - 1;
}
return dstlen;
@ -634,58 +646,59 @@ static inline uint32_t get_serial(const uint8_t *const packet)
return serial;
}
static void t1_c1_packet_decoder(unsigned bit, unsigned rssi)
static void t1_c1_packet_decoder(unsigned bit, unsigned rssi, struct t1_c1_packet_decoder_work *decoder, const char *algorithm)
{
t1_c1_packet_decoder_work.current_rssi = rssi;
decoder->current_rssi = rssi;
(*t1_c1_packet_decoder_work.state++)(bit);
(*decoder->state++)(bit, decoder);
if (*t1_c1_packet_decoder_work.state == idle)
if (*decoder->state == idle)
{
// nothing
}
else if (*t1_c1_packet_decoder_work.state == done)
else if (*decoder->state == done)
{
if (t1_c1_packet_decoder_work.b_frame_type)
if (decoder->b_frame_type)
{
t1_c1_packet_decoder_work.crc_ok = check_calc_crc_wmbus_b_frame_type(t1_c1_packet_decoder_work.packet, t1_c1_packet_decoder_work.L) ? 1 : 0;
decoder->crc_ok = check_calc_crc_wmbus_b_frame_type(decoder->packet, decoder->L) ? 1 : 0;
}
else
{
t1_c1_packet_decoder_work.crc_ok = check_calc_crc_wmbus(t1_c1_packet_decoder_work.packet, t1_c1_packet_decoder_work.L) ? 1 : 0;
decoder->crc_ok = check_calc_crc_wmbus(decoder->packet, decoder->L) ? 1 : 0;
}
fprintf(stdout, "%s;%u;%u;%s;%u;%u;%08X;", t1_c1_packet_decoder_work.c1_packet ? "C1": "T1",
t1_c1_packet_decoder_work.crc_ok,
t1_c1_packet_decoder_work.err_3outof^1,
t1_c1_packet_decoder_work.timestamp,
t1_c1_packet_decoder_work.packet_rssi,
algorithm = ""; // uncomment of want to see which algorithm is executed right now
fprintf(stdout, "%s%s;%u;%u;%s;%u;%u;%08X;", algorithm, decoder->c1_packet ? "C1": "T1",
decoder->crc_ok,
decoder->err_3outof^1,
decoder->timestamp,
decoder->packet_rssi,
rssi,
get_serial(t1_c1_packet_decoder_work.packet));
get_serial(decoder->packet));
#if 0
fprintf(stdout, "0x");
for (size_t l = 0; l < t1_c1_packet_decoder_work.L; l++) fprintf(stdout, "%02x", t1_c1_packet_decoder_work.packet[l]);
for (size_t l = 0; l < decoder->L; l++) fprintf(stdout, "%02x", decoder->packet[l]);
fprintf(stdout, ";");
#endif
#if 1
if (t1_c1_packet_decoder_work.b_frame_type)
if (decoder->b_frame_type)
{
t1_c1_packet_decoder_work.L = cook_pkt_b_frame_type(t1_c1_packet_decoder_work.packet, t1_c1_packet_decoder_work.L);
decoder->L = cook_pkt_b_frame_type(decoder->packet, decoder->L);
}
else
{
t1_c1_packet_decoder_work.L = cook_pkt(t1_c1_packet_decoder_work.packet, t1_c1_packet_decoder_work.L);
decoder->L = cook_pkt(decoder->packet, decoder->L);
}
fprintf(stdout, "0x");
for (size_t l = 0; l < t1_c1_packet_decoder_work.L; l++) fprintf(stdout, "%02x", t1_c1_packet_decoder_work.packet[l]);
for (size_t l = 0; l < decoder->L; l++) fprintf(stdout, "%02x", decoder->packet[l]);
#endif
fprintf(stdout, "\n");
fflush(stdout);
reset_t1_c1_packet_decoder();
reset_t1_c1_packet_decoder(decoder);
}
else
{
@ -693,7 +706,7 @@ static void t1_c1_packet_decoder(unsigned bit, unsigned rssi)
// The current packet seems to be collided with an another one.
if (rssi < PACKET_CAPTURE_THRESHOLD)
{
reset_t1_c1_packet_decoder();
reset_t1_c1_packet_decoder(decoder);
}
}
}