|
/* |
|
* weather.c -- receiver for ascot weather station |
|
* |
|
* Copyright (c) 2012, Alex Wilson (arekinath) |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions are met: |
|
* * Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* * Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* * Neither the name of the <organization> nor the |
|
* names of its contributors may be used to endorse or promote products |
|
* derived from this software without specific prior written permission. |
|
* |
|
* See the standard BSD license disclaimer for details about warranty, liability, |
|
* fitness for purpose, etc. |
|
*/ |
|
|
|
#include <rtl-sdr.h> |
|
#include <stdio.h> |
|
#include <stdint.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <math.h> |
|
|
|
int |
|
main(int argc, char *argv[]) |
|
{ |
|
rtlsdr_dev_t *dev; |
|
int ret; |
|
|
|
if (rtlsdr_open(&dev, 0)) { |
|
fprintf(stderr, "failed to open rtlsdr\n"); |
|
return 1; |
|
} |
|
|
|
/* 433.798 MHz is the channel 1 freq (approximately) */ |
|
const int32_t freq = 433798000; |
|
|
|
/* tune 100kHz above the target */ |
|
if (rtlsdr_set_center_freq(dev, freq - 100000)) { |
|
fprintf(stderr, "failed to set centre\n"); |
|
return 1; |
|
} |
|
|
|
/* ask for it back so we know the actual centre */ |
|
int32_t rfreq = rtlsdr_get_center_freq(dev); |
|
int32_t devn = freq - rfreq; |
|
|
|
uint32_t samprate = 1048576; |
|
if (rtlsdr_set_sample_rate(dev, samprate)) { |
|
fprintf(stderr, "failed set sample rate\n"); |
|
return 1; |
|
} |
|
|
|
samprate = rtlsdr_get_sample_rate(dev); |
|
const uint32_t centre = rtlsdr_get_center_freq(dev); |
|
|
|
/* xxx: sort out what tuner gain to use */ |
|
rtlsdr_set_tuner_gain(dev, 490); |
|
|
|
const int readsize = 1024; |
|
|
|
uint8_t *buf = malloc(readsize); |
|
memset(buf, 0, readsize); |
|
|
|
/* generate the ~100kHz oscillator to mix the incoming signal with */ |
|
double *wave = malloc(sizeof(double)*readsize); |
|
for (int i = 0; i < (readsize / 2); ++i) { |
|
double t = (devn * (double)i) / (double)samprate; |
|
wave[i*2] = cos(2*3.14159*t); |
|
wave[i*2+1] = sin(2*3.14159*t); |
|
} |
|
|
|
double *demod = malloc(sizeof(double)*readsize); |
|
double *demod_smooth = malloc(sizeof(double)*readsize); |
|
|
|
int lenout; |
|
|
|
/* always have to call reset_buffer before reading */ |
|
if (rtlsdr_reset_buffer(dev)) { |
|
fprintf(stderr, "failed reset buffer\n"); |
|
return 1; |
|
} |
|
|
|
/* number of reads we've been 'high' for */ |
|
uint64_t highfor = 0; |
|
uint64_t lowfor = 0; |
|
|
|
/* was there a pulse before the last change? */ |
|
uint8_t pulse = 0; |
|
|
|
/* frame state */ |
|
uint64_t last_last_frame = 1; |
|
uint64_t last_frame = 1; |
|
uint64_t frame = 0; |
|
uint8_t bitno = 0; |
|
|
|
while (1) { |
|
/* fill up the buffer with incoming I/Q samples */ |
|
ret = rtlsdr_read_sync(dev, buf, readsize, &lenout); |
|
if (ret || lenout < readsize) { |
|
fprintf(stderr, "short read! (=%d) %d/%d\n", ret, lenout, readsize); |
|
return 1; |
|
} |
|
|
|
/* mix with the local oscillator to downconvert it. also rectify at the same time */ |
|
for (int i = 0; i < readsize; ++i) { |
|
demod[i] = ((((double)buf[i]) / 256.0) - 0.5) * wave[i]; |
|
if (demod[i] < 0) demod[i] = 0; |
|
} |
|
|
|
/* now do a moving average filter to smooth the resulting waveform */ |
|
for (int i = 0; i < 20; ++i) |
|
demod_smooth[i] = demod[i]; |
|
|
|
for (int i = 20; i < readsize; ++i) |
|
demod_smooth[i] = (demod[i] + demod[i-2] + demod[i-4] + demod[i-6] + demod[i-8] + demod[i-10] + demod[i-12] + demod[i-14] + demod[i-16] + demod[i-18] + demod[i-20]) / 11; |
|
|
|
/* sum the mags of the samples (don't bother squaring, since we rectified it) */ |
|
double magsum = 0; |
|
for (int i = 0; i < readsize; i+=2) { |
|
double mag = demod_smooth[i] + demod_smooth[i+1]; |
|
magsum += mag * mag; |
|
} |
|
|
|
/* xxx: arbitrary constant warning! somewhere from 0.5 - 1.5 seems to work */ |
|
if (magsum > 0.7) { |
|
/* this is a 'high' sample, as in during a pulse */ |
|
|
|
/* is this the first 'high' sample? ie, did we just end a gap? */ |
|
if (highfor == 0) { |
|
const uint64_t one64 = 1; |
|
|
|
/* zeros take 1-2 samples */ |
|
if (pulse && lowfor >= 1 && lowfor <= 2) { |
|
bitno--; |
|
frame &= ~(one64<<bitno); |
|
|
|
/* ones take 3-5 samples */ |
|
} else if (pulse && lowfor >= 3 && lowfor <= 5) { |
|
bitno--; |
|
frame |= (one64<<bitno); |
|
|
|
/* and 6-8 samples is an 'X' */ |
|
} else if (pulse && lowfor >= 6 && lowfor <= 8) { |
|
|
|
if (last_frame == frame && last_last_frame == frame && bitno == 0) { |
|
const uint64_t mask12 = 0xfff; |
|
const uint64_t mask7 = 0x7f; |
|
uint64_t preamble = (frame >> 24) & mask12; |
|
if (preamble == 0xae8) { |
|
uint64_t temp = (frame >> 12) & mask12; |
|
uint16_t tempwhole = temp / 10; |
|
uint16_t tempfrac = temp % 10; |
|
uint64_t humid = frame & mask7; |
|
printf("N:%hu.%hu:%llu\n", tempwhole, tempfrac, humid); |
|
break; |
|
} |
|
} |
|
|
|
last_last_frame = last_frame; |
|
last_frame = frame; |
|
frame = 0; |
|
bitno = 36; |
|
} |
|
|
|
pulse = 0; |
|
lowfor = 0; |
|
} |
|
|
|
/* increment the counter for high samples in a row */ |
|
highfor++; |
|
|
|
} else { |
|
/* this is a low sample */ |
|
|
|
/* is this the first? did we just end a pulse? */ |
|
if (lowfor == 0) { |
|
if (highfor >= 1 && highfor <= 3) { |
|
pulse = 1; |
|
} else { |
|
pulse = 0; |
|
} |
|
highfor = 0; |
|
} |
|
lowfor++; |
|
} |
|
|
|
} |
|
|
|
/* xxx: todo: cleanup other stuff */ |
|
rtlsdr_close(dev); |
|
} |