diff --git a/Makefile b/Makefile index 59ff524..159b197 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ $(shell $(MKDIR) -p $(OUTDIR)) # Create a version number based on the latest git tag. COMMIT_HASH?=$(shell git log --pretty=format:'%H' -n 1) -TAG?=$(shell git describe --tags) +TAG?=$(shell git describe --tags --always) BRANCH?=$(shell git rev-parse --abbrev-ref HEAD) CHANGES?=$(shell git status -s | grep -v '?? ') @@ -62,7 +62,7 @@ pi1: rebuild: clean all install: release - cp $(OUTFILE) /usr/bin + cp -f $(OUTFILE) /usr/bin clean: $(RM) -rf "$(OUTDIR)" diff --git a/build/rtl_wmbus_x64.exe b/build/rtl_wmbus_x64.exe index 53985f7..6f232e8 100755 Binary files a/build/rtl_wmbus_x64.exe and b/build/rtl_wmbus_x64.exe differ diff --git a/build/rtl_wmbus_x86.exe b/build/rtl_wmbus_x86.exe index 78515f8..931d799 100755 Binary files a/build/rtl_wmbus_x86.exe and b/build/rtl_wmbus_x86.exe differ diff --git a/rtl_wmbus.c b/rtl_wmbus.c index d4fed4f..beb4bdc 100644 --- a/rtl_wmbus.c +++ b/rtl_wmbus.c @@ -55,6 +55,14 @@ #include "net_support.h" #endif +#ifndef TIME2_ALGORITHM_ENABLED +#define TIME2_ALGORITHM_ENABLED 1 +#endif + +#ifndef RUN_LENGTH_ALGORITHM_ENABLED +#define RUN_LENGTH_ALGORITHM_ENABLED 1 +#endif + static const uint32_t ACCESS_CODE_T1_C1 = 0b0101010101010000111101u; static const uint32_t ACCESS_CODE_T1_C1_BITMASK = 0x3FFFFFu; static const unsigned ACCESS_CODE_T1_C1_ERRORS = 1u; // 0 if no errors allowed @@ -786,11 +794,13 @@ static void time2_algorithm_s1(unsigned bit, unsigned rssi, struct time2_algorit static int opts_run_length_algorithm_enabled = 1; -static int opts_time2_algorithm_enabled = 1; +static int opts_time2_algorithm_enabled = TIME2_ALGORITHM_ENABLED; static unsigned opts_decimation_rate = 2u; static int opts_s1_t1_c1_simultaneously = 0; static int opts_accurate_atan = 1; int opts_show_used_algorithm = 0; +static int opts_t1_c1_processing_enabled = 1; +static int opts_s1_processing_enabled = 1; static const unsigned opts_CLOCK_LOCK_THRESHOLD_T1_C1 = 2; // Is not implemented as option yet. static const unsigned opts_CLOCK_LOCK_THRESHOLD_S1 = 2; // Is not implemented as option yet. @@ -806,6 +816,7 @@ static void print_usage(const char *program_name) fprintf(stdout, "\t-v show used algorithm in the output\n"); fprintf(stdout, "\t-V show version\n"); fprintf(stdout, "\t-s receive S1 and T1/C1 datagrams simultaneously. rtl_sdr _MUST_ be set to 868.625MHz (-f 868.625M)\n"); + fprintf(stdout, "\t-p [T,S] to disable processing T1/C1 or S1 mode.\n"); } static void print_version(void) @@ -818,13 +829,28 @@ static void process_options(int argc, char *argv[]) { int option; - while ((option = getopt(argc, argv, "ad:r:vVst:")) != -1) + while ((option = getopt(argc, argv, "ad:p:r:vVst:")) != -1) { switch (option) { case 'a': opts_accurate_atan = 0; break; + case 'p': + if (strcmp(optarg, "T") == 0 || strcmp(optarg, "t") == 0) + { + opts_t1_c1_processing_enabled = 0; + } + else if (strcmp(optarg, "S") == 0 || strcmp(optarg, "s") == 0) + { + opts_s1_processing_enabled = 0; + } + else + { + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + break; case 'r': if (strcmp(optarg, "0") == 0) { @@ -931,6 +957,190 @@ static void shift_freq_plus_minus325(float *iplus, float *qplus, float *iminus, *qminus = qx - iz; } +typedef void (*t1_c1_signal_chain_prototype)(float i_t1_c1, float q_t1_c1, + struct time2_algorithm_t1_c1 *t2_algo_t1_c1, + struct runlength_algorithm_t1_c1 *rl_algo_t1_c1, + float (*polar_discriminator_t1_c1_function)(float i, float q)); + +void t1_c1_signal_chain(float i_t1_c1, float q_t1_c1, + struct time2_algorithm_t1_c1 *t2_algo_t1_c1, + struct runlength_algorithm_t1_c1 *rl_algo_t1_c1, + float (*polar_discriminator_t1_c1_function)(float i, float q)) +{ + static int16_t old_clock_t1_c1 = INT16_MIN; + static unsigned clock_lock_t1_c1 = 0; + + // Demodulate. + const float _delta_phi_t1_c1 = polar_discriminator_t1_c1_function(i_t1_c1, q_t1_c1); + //int16_t demodulated_signal = (INT16_MAX-1)*delta_phi; + //fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out); + + // Post-filtering to prevent bit errors because of signal jitter. + const float delta_phi_t1_c1 = lp_fir_butter_800kHz_100kHz_160kHz(_delta_phi_t1_c1); + //const float delta_phi_t1_c1 = equalizer_t1_c1(_delta_phi_t1_c1, _delta_phi_t1_c1 >= 0.f ? 1.f : -1.f); + //const float delta_phi_s1 = equalizer_s1(_delta_phi_s1, _delta_phi_s1 >= 0.f ? 1.f : -1.f); + //int16_t demodulated_signal = (INT16_MAX-1)*delta_phi; + //fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out2); + + // Get the bit! + unsigned bit_t1_c1 = (delta_phi_t1_c1 >= 0) ? (1u<= 0) ? INT16_MAX : INT16_MIN; + //fwrite(&clock, sizeof(clock), 1, clock_out); + + if (clock_t1_c1 > old_clock_t1_c1) + { // Clock signal rising edge detected. + clock_lock_t1_c1 = 1; + } + else if (clock_t1_c1 == INT16_MAX) + { // Clock signal is still high. + if (clock_lock_t1_c1 < opts_CLOCK_LOCK_THRESHOLD_T1_C1) + { // Skip up to (opts_CLOCK_LOCK_THRESHOLD_T1_C1 - 1) clock bits + // to get closer to the middle of the data bit. + clock_lock_t1_c1++; + } + else if (clock_lock_t1_c1 == opts_CLOCK_LOCK_THRESHOLD_T1_C1) + { // Sample data bit at CLOCK_LOCK_THRESHOLD_T1_C1 clock bit position. + clock_lock_t1_c1++; + time2_algorithm_t1_c1(bit_t1_c1, rssi_t1_c1, t2_algo_t1_c1); + //int16_t u = bit ? (INT16_MAX-1) : 0; + //fwrite(&u, sizeof(u), 1, bits_out); + } + } + old_clock_t1_c1 = clock_t1_c1; + // --- clock recovery section end --- + } + #endif + // --- time2 algorithm section end --- +} + +void t1_c1_signal_chain_empty(float i_t1_c1, float q_t1_c1, + struct time2_algorithm_t1_c1 *t2_algo_t1_c1, + struct runlength_algorithm_t1_c1 *rl_algo_t1_c1, + float (*polar_discriminator_t1_c1_function)(float i, float q)) +{ +} + +typedef void (*s1_signal_chain_prototype)(float i_s1, float q_s1, + struct time2_algorithm_s1 *t2_algo_s1, + struct runlength_algorithm_s1 *rl_algo_s1, + float (*polar_discriminator_s1_function)(float i, float q)); + +void s1_signal_chain(float i_s1, float q_s1, + struct time2_algorithm_s1 *t2_algo_s1, + struct runlength_algorithm_s1 *rl_algo_s1, + float (*polar_discriminator_s1_function)(float i, float q)) +{ + static int16_t old_clock_s1 = INT16_MIN; + static unsigned clock_lock_s1 = 0; + + // Demodulate. + const float _delta_phi_s1 = polar_discriminator_s1_function(i_s1, q_s1); + //int16_t demodulated_signal = (INT16_MAX-1)*delta_phi; + //fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out); + + // Post-filtering to prevent bit errors because of signal jitter. + const float delta_phi_s1 = lp_fir_butter_800kHz_32kHz_36kHz(_delta_phi_s1); + //const float delta_phi_t1_c1 = equalizer_t1_c1(_delta_phi_t1_c1, _delta_phi_t1_c1 >= 0.f ? 1.f : -1.f); + //const float delta_phi_s1 = equalizer_s1(_delta_phi_s1, _delta_phi_s1 >= 0.f ? 1.f : -1.f); + //int16_t demodulated_signal = (INT16_MAX-1)*delta_phi; + //fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out2); + + // Get the bit! + unsigned bit_s1 = (delta_phi_s1 >= 0) ? (1u<= 0) ? INT16_MAX : INT16_MIN; + //fwrite(&clock, sizeof(clock), 1, clock_out); + + if (clock_s1 > old_clock_s1) + { // Clock signal rising edge detected. + clock_lock_s1 = 1; + } + else if (clock_s1 == INT16_MAX) + { // Clock signal is still high. + if (clock_lock_s1 < opts_CLOCK_LOCK_THRESHOLD_S1) + { // Skip up to (opts_CLOCK_LOCK_THRESHOLD_S1 - 1) clock bits + // to get closer to the middle of the data bit. + clock_lock_s1++; + } + else if (clock_lock_s1 == opts_CLOCK_LOCK_THRESHOLD_S1) + { // Sample data bit at CLOCK_LOCK_THRESHOLD_S1 clock bit position. + clock_lock_s1++; + time2_algorithm_s1(bit_s1, rssi_s1, t2_algo_s1); + //int16_t u = bit ? (INT16_MAX-1) : 0; + //fwrite(&u, sizeof(u), 1, bits_out); + } + } + old_clock_s1 = clock_s1; + // --- clock recovery section end --- + } + #endif + // --- time2 algorithm section end --- +} + +void s1_signal_chain_empty(float i_s1, float q_s1, + struct time2_algorithm_s1 *t2_algo_s1, + struct runlength_algorithm_s1 *rl_algo_s1, + float (*polar_discriminator_s1_function)(float i, float q)) +{ +} + int main(int argc, char *argv[]) { #if WINDOWS_BUILD == 1 @@ -943,9 +1153,9 @@ int main(int argc, char *argv[]) const int fs_kHz = opts_decimation_rate*800; // Sample rate [kHz] as a multiple of 800 kHz. float i_t1_c1, q_t1_c1; float i_s1, q_s1; + + unsigned decimation_rate_index = 0; - int16_t old_clock_t1_c1 = INT16_MIN, old_clock_s1 = INT16_MIN; - unsigned clock_lock_t1_c1 = 0, clock_lock_s1 = 0; struct time2_algorithm_t1_c1 t2_algo_t1_c1; time2_algorithm_t1_c1_reset(&t2_algo_t1_c1); @@ -959,6 +1169,9 @@ int main(int argc, char *argv[]) struct runlength_algorithm_s1 rl_algo_s1; runlength_algorithm_reset_s1(&rl_algo_s1); + t1_c1_signal_chain_prototype process_t1_c1_chain = opts_t1_c1_processing_enabled ? t1_c1_signal_chain: t1_c1_signal_chain_empty; + s1_signal_chain_prototype process_s1_chain = opts_s1_processing_enabled ? s1_signal_chain : s1_signal_chain_empty; + float (*polar_discriminator_t1_c1_function)(float i, float q) = opts_accurate_atan ? polar_discriminator_t1_c1 : polar_discriminator_t1_c1_inaccurate; float (*polar_discriminator_s1_function)(float i, float q) = opts_accurate_atan ? polar_discriminator_s1 : polar_discriminator_s1_inaccurate; @@ -995,8 +1208,8 @@ int main(int argc, char *argv[]) for (size_t k = 0; k < sizeof(samples)/sizeof(samples[0]); k += 2) // +2 : i and q interleaved { - const float i_unfilt = ((float)samples[k] - 127.5f); - const float q_unfilt = ((float)samples[k + 1] - 127.5f); + const float i_unfilt = ((float)(samples[k]) - 127.5f); + const float q_unfilt = ((float)(samples[k + 1]) - 127.5f); // rtl_sdr -f 868.35M -s 2400000 - 2>/dev/null | build/rtl_wmbus -d 3 //shift_freq(&i_unfilt, &q_unfilt, 600, 2400); @@ -1015,145 +1228,30 @@ int main(int argc, char *argv[]) // Low-Pass-Filtering before decimation is necessary, to ensure // that i and q signals don't contain frequencies above new sample - // rate. - // The sample rate decimation is realised as sum over i and q, - // which must not be divided by decimation factor before - // demodulating (atan2(q,i)). -#if 0 - i_t1_c1 = lp_fir_butter_1600kHz_160kHz_200kHz_t1_c1(i_t1_c1_unfilt, 0); - q_t1_c1 = lp_fir_butter_1600kHz_160kHz_200kHz_t1_c1(q_t1_c1_unfilt, 1); - - i_s1 = lp_fir_butter_1600kHz_160kHz_200kHz_s1(i_s1_unfilt, 0); - q_s1 = lp_fir_butter_1600kHz_160kHz_200kHz_s1(q_s1_unfilt, 1); -#elif 0 - i = lp_ppf_butter_1600kHz_160kHz_200kHz(i_unfilt, 0); - q = lp_ppf_butter_1600kHz_160kHz_200kHz(q_unfilt, 1); -#elif 0 - i = lp_firfp_butter_1600kHz_160kHz_200kHz(i_unfilt, 0); - q = lp_firfp_butter_1600kHz_160kHz_200kHz(q_unfilt, 1); -#elif 0 - i = lp_ppffp_butter_1600kHz_160kHz_200kHz(i_unfilt, 0); - q = lp_ppffp_butter_1600kHz_160kHz_200kHz(q_unfilt, 1); -#else - // Moving average can be viewed as a low pass filter. - + // rate. Moving average can be viewed as a low pass filter. i_t1_c1 = moving_average_t1_c1(i_t1_c1_unfilt, 0); q_t1_c1 = moving_average_t1_c1(q_t1_c1_unfilt, 1); - i_s1 = moving_average_s1(i_s1_unfilt, 0); - q_s1 = moving_average_s1(q_s1_unfilt, 1); -#endif - #if 0 equalizer_complex_t1_c1(&i_t1_c1, &q_t1_c1); #endif + // Low-Pass-Filtering before decimation is necessary, to ensure + // that i and q signals don't contain frequencies above new sample + // rate. Moving average can be viewed as a low pass filter. + i_s1 = moving_average_s1(i_s1_unfilt, 0); + q_s1 = moving_average_s1(q_s1_unfilt, 1); + + #if 0 + equalizer_complex_s1(&i_s1, &q_s1); // FIXME: Function does not exist. + #endif + ++decimation_rate_index; if (decimation_rate_index < opts_decimation_rate) continue; decimation_rate_index = 0; - // Demodulate. - const float _delta_phi_t1_c1 = polar_discriminator_t1_c1_function(i_t1_c1, q_t1_c1); - const float _delta_phi_s1 = polar_discriminator_s1_function(i_s1, q_s1); - //int16_t demodulated_signal = (INT16_MAX-1)*delta_phi; - //fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out); - - // Post-filtering to prevent bit errors because of signal jitter. - const float delta_phi_t1_c1 = lp_fir_butter_800kHz_100kHz_160kHz(_delta_phi_t1_c1); - const float delta_phi_s1 = lp_fir_butter_800kHz_32kHz_36kHz(_delta_phi_s1); - //const float delta_phi_t1_c1 = equalizer_t1_c1(_delta_phi_t1_c1, _delta_phi_t1_c1 >= 0.f ? 1.f : -1.f); - //const float delta_phi_s1 = equalizer_s1(_delta_phi_s1, _delta_phi_s1 >= 0.f ? 1.f : -1.f); - //int16_t demodulated_signal = (INT16_MAX-1)*delta_phi; - //fwrite(&demodulated_signal, sizeof(demodulated_signal), 1, demod_out2); - - // Get the bit! - unsigned bit_t1_c1 = (delta_phi_t1_c1 >= 0) ? (1u<= 0) ? (1u<= 0) ? INT16_MAX : INT16_MIN; - const int16_t clock_s1 = (bp_iir_cheb1_800kHz_22kHz_30kHz_34kHz_42kHz(delta_phi_s1 * delta_phi_s1) >= 0) ? INT16_MAX : INT16_MIN; - //fwrite(&clock, sizeof(clock), 1, clock_out); - - if (clock_t1_c1 > old_clock_t1_c1) - { // Clock signal rising edge detected. - clock_lock_t1_c1 = 1; - } - else if (clock_t1_c1 == INT16_MAX) - { // Clock signal is still high. - if (clock_lock_t1_c1 < opts_CLOCK_LOCK_THRESHOLD_T1_C1) - { // Skip up to (opts_CLOCK_LOCK_THRESHOLD_T1_C1 - 1) clock bits - // to get closer to the middle of the data bit. - clock_lock_t1_c1++; - } - else if (clock_lock_t1_c1 == opts_CLOCK_LOCK_THRESHOLD_T1_C1) - { // Sample data bit at CLOCK_LOCK_THRESHOLD_T1_C1 clock bit position. - clock_lock_t1_c1++; - time2_algorithm_t1_c1(bit_t1_c1, rssi_t1_c1, &t2_algo_t1_c1); - //int16_t u = bit ? (INT16_MAX-1) : 0; - //fwrite(&u, sizeof(u), 1, bits_out); - } - } - old_clock_t1_c1 = clock_t1_c1; - - if (clock_s1 > old_clock_s1) - { // Clock signal rising edge detected. - clock_lock_s1 = 1; - } - else if (clock_s1 == INT16_MAX) - { // Clock signal is still high. - if (clock_lock_s1 < opts_CLOCK_LOCK_THRESHOLD_S1) - { // Skip up to (opts_CLOCK_LOCK_THRESHOLD_S1 - 1) clock bits - // to get closer to the middle of the data bit. - clock_lock_s1++; - } - else if (clock_lock_s1 == opts_CLOCK_LOCK_THRESHOLD_S1) - { // Sample data bit at CLOCK_LOCK_THRESHOLD_S1 clock bit position. - clock_lock_s1++; - time2_algorithm_s1(bit_s1, rssi_s1, &t2_algo_s1); - //int16_t u = bit ? (INT16_MAX-1) : 0; - //fwrite(&u, sizeof(u), 1, bits_out); - } - } - old_clock_s1 = clock_s1; - // --- clock recovery section end --- - } - #endif - // --- time2 algorithm section end --- + process_t1_c1_chain(i_t1_c1, q_t1_c1, &t2_algo_t1_c1, &rl_algo_t1_c1, polar_discriminator_t1_c1_function); + process_s1_chain(i_s1, q_s1, &t2_algo_s1, &rl_algo_s1, polar_discriminator_s1_function); } } diff --git a/rtl_wmbus.py b/rtl_wmbus.py new file mode 100755 index 0000000..a5ea4ce --- /dev/null +++ b/rtl_wmbus.py @@ -0,0 +1,73 @@ +import sys +import os +import traceback +import subprocess +import psutil +import signal +from time import time + + +__rtl_sdr = None +__rtl_wmbus = None + + +def __signal_handler(signal, frame): + global __rtl_sdr, __rtl_wmbus + + if __rtl_wmbus: + #print("Terminating rtl_wmbus") + __rtl_wmbus.kill() + __rtl_wmbus.wait() + + if __rtl_sdr: + #print("Terminating rtl_sdr") + __rtl_sdr.kill() + __rtl_sdr.wait() + + +def _main(args): + global __rtl_sdr, __rtl_wmbus + + #"/C/Program Files/PothosSDR/bin/rtl_sdr" -f 868.95M -s 1600000 - 2>/dev/null | build/rtl_wmbus_x64.exe -v + #"C:\Program Files\PothosSDR\bin\rtl_sdr" -f 868.95M -s 1600000 - 2>NUL | build\rtl_wmbus_x64.exe -v + + try: + signal.signal(signal.SIGINT, __signal_handler) + + __rtl_sdr = subprocess.Popen([r"c:\Program Files\PothosSDR\bin\rtl_sdr", "-f", "868.95M", "-s", "1600000", "-"], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + psutil.Process(__rtl_sdr.pid).nice(psutil.REALTIME_PRIORITY_CLASS) + + __rtl_wmbus = subprocess.Popen([r"d:\rtl-wmbus\build\rtl_wmbus_x64.exe", "-v"], stdin=__rtl_sdr.stdout, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + psutil.Process(__rtl_wmbus.pid).nice(psutil.REALTIME_PRIORITY_CLASS) + + psutil.Process(os.getpid()).nice(psutil.REALTIME_PRIORITY_CLASS) + + #__rtl_sdr.stdout.close() # Allow rtl_sdr to receive a SIGPIPE if rtl_wmbus exits. + + t1 = time() + rate = 0 + while __rtl_sdr.poll() is None and __rtl_wmbus.poll() is None: + s = __rtl_wmbus.stdout.readline().decode('utf-8').rstrip('\n').rstrip('\r') + t2 = time() + + if s.find("T1;1") >= 0 or s.find("C1;1") >= 0 or s.find("S1;1") >= 0: + rate += 1 + print(s) + else: + pass + + dt = t2 - t1 + if dt >= 10: + #print(rate/dt, file=sys.stderr) + t1 = t2 + rate = 0 + + return 0 + + except Exception as e: + print(traceback.print_exc()) + return 1 + + +if __name__ == '__main__': + sys.exit(_main(sys.argv))