Rebase to latest upstream demods.

pull/276/head
Mark Jessop 2020-05-23 16:36:20 +09:30
rodzic c98633d551
commit 7b17f93ce2
20 zmienionych plików z 1815 dodań i 303 usunięć

Wyświetl plik

@ -459,7 +459,7 @@ def telemetry_filter(telemetry):
meisei_callsign_valid = False
# If Vaisala or DFMs, check the callsigns are valid. If M10, iMet or LMS6, just pass it through.
if vaisala_callsign_valid or dfm_callsign_valid or meisei_callsign_valid or ('M10' in telemetry['type']) or ('MK2LMS' in telemetry['type']) or ('LMS6' in telemetry['type']) or ('iMet' in telemetry['type']) or ('UDP' in telemetry['type']):
if vaisala_callsign_valid or dfm_callsign_valid or meisei_callsign_valid or ('M10' in telemetry['type']) or ('LMS' in telemetry['type']) or ('IMET' in telemetry['type']):
return True
else:
_id_msg = "Payload ID %s is invalid." % telemetry['id']

Wyświetl plik

@ -61,11 +61,11 @@ def telemetry_to_aprs_position(sonde_data, object_name="<id>", aprs_comment="BOM
# Use the generated id same as dxlAPRS
_object_name = sonde_data['aprsid']
elif 'iMet' in sonde_data['type']:
elif 'IMET' in sonde_data['type']:
# Use the last 5 characters of the unique ID we have generated.
_object_name = "IMET" + sonde_data['id'][-5:]
elif ('MK2LMS' in sonde_data['type']) or ('LMS6' in sonde_data['type']):
elif 'LMS' in sonde_data['type']:
# Use the last 5 hex digits of the sonde ID.
_id_suffix = int(sonde_data['id'].split('-')[1])
_id_hex = hex(_id_suffix).upper()
@ -83,6 +83,7 @@ def telemetry_to_aprs_position(sonde_data, object_name="<id>", aprs_comment="BOM
# New Sonde types will be added in here.
else:
# Unknown sonde type, don't know how to handle this yet.
logging.error('No APRS ID conversion available for sonde type: %s' % sonde_data['type'])
return (None, None)
else:
_object_name = object_name

Wyświetl plik

@ -793,6 +793,12 @@ class SondeDecoder(object):
self.log_error("Invalid date/time in telemetry dict - %s (Sonde may not have GPS lock)" % str(e))
return False
if self.sonde_type == 'UDP':
# If we are accepting sondes via UDP, we make use of the 'type' field provided by
# the decoder.
# Note that the types returned by the
self.sonde_type = _telemetry['type']
# Add in the sonde type field.
if 'subtype' in _telemetry:
if self.sonde_type == 'RS41':
@ -817,13 +823,15 @@ class SondeDecoder(object):
_telemetry['sdr_device_idx'] = self.device_idx
# Check for an 'aux' field, this indicates that the sonde has an auxilliary payload,
# which is most likely an Ozone sensor. We append -Ozone to the sonde type field to indicate this.
# which is most likely an Ozone sensor (though could be something different!)
# We append -Ozone to the sonde type field to indicate this.
# TODO: Decode device ID from aux field to indicate what the aux payload actually is?
if 'aux' in _telemetry:
_telemetry['type'] += "-Ozone"
# iMet Specific actions
if self.sonde_type == 'iMet':
if self.sonde_type == 'IMET':
# Check we have GPS lock.
if _telemetry['sats'] < 4:
# No GPS lock means an invalid time, which means we can't accurately calculate a unique ID.
@ -842,8 +850,8 @@ class SondeDecoder(object):
_telemetry['station_code'] = self.imet_location
# LMS6 Specific Actions
if self.sonde_type == 'MK2LMS' or self.sonde_type == 'LMS6':
# LMS Specific Actions (LMS6, MK2LMS)
if 'LMS' in self.sonde_type:
# We are only provided with HH:MM:SS, so the timestamp needs to be fixed, just like with the iMet sondes
_telemetry['datetime_dt'] = fix_datetime(_telemetry['datetime'])

Wyświetl plik

@ -101,13 +101,6 @@ def sonde_telemetry_to_sentence(telemetry, payload_callsign=None, comment=None):
if (telemetry['bt'] != -1) and (telemetry['bt'] != 65535):
_sentence += " BT %s" % time.strftime("%H:%M:%S", time.gmtime(telemetry['bt']))
# NOTE: Disabled as of 2019-09-21
# Add on the station code, which will only be present if we are receiving an iMet sonde.
# This may assist multiple receiving stations in the vicinity of an iMet launch site coordinate
# the iMet unique ID generation.
#if 'station_code' in telemetry:
# _sentence += " LOC: %s" % telemetry['station_code']
# Add on any custom comment data if provided.
if comment != None:
comment = comment.replace(',','_')

Wyświetl plik

@ -197,6 +197,7 @@ def detect_sonde(frequency, rs_path="./", dwell_time=10, sdr_fm='rtl_fm', device
'RS92' - Vaisala RS92
'DFM' - Graw DFM06 / DFM09 (similar telemetry formats)
'M10' - MeteoModem M10
'M20' - MeteoModem M20
'iMet' - interMet iMet
'MK2LMS' - LMS6, 1680 MHz variant (using MK2A 9600 baud telemetry)
@ -345,9 +346,12 @@ def detect_sonde(frequency, rs_path="./", dwell_time=10, sdr_fm='rtl_fm', device
elif 'M10' in _type:
logging.debug("Scanner #%s - Detected a M10 Sonde! (Score: %.2f, Offset: %.1f Hz)" % (str(device_idx), _score, _offset_est))
_sonde_type = "M10"
elif 'M20' in _type:
logging.debug("Scanner #%s - Detected a M20 Sonde! (Not yet supported...) (Score: %.2f, Offset: %.1f Hz)" % (str(device_idx), _score, _offset_est))
_sonde_type = "M20"
elif 'IMET4' in _type:
logging.debug("Scanner #%s - Detected a iMet-4 Sonde! (Score: %.2f, Offset: %.1f Hz)" % (str(device_idx), _score, _offset_est))
_sonde_type = "iMet"
_sonde_type = "IMET"
elif 'IMET1' in _type:
logging.debug("Scanner #%s - Detected a iMet Sonde! (Type %s - Unsupported) (Score: %.2f)" % (str(device_idx), _type, _score))
_sonde_type = "IMET1"
@ -355,7 +359,7 @@ def detect_sonde(frequency, rs_path="./", dwell_time=10, sdr_fm='rtl_fm', device
logging.debug("Scanner #%s - Detected a LMS6 Sonde! (Score: %.2f, Offset: %.1f Hz)" % (str(device_idx), _score, _offset_est))
_sonde_type = "LMS6"
elif 'C34' in _type:
logging.debug("Scanner #%s - Detected a Meteolabor C34/C50 Sonde! (Unsupported) (Score: %.2f)" % (str(device_idx), _score))
logging.debug("Scanner #%s - Detected a Meteolabor C34/C50 Sonde! (Not yet supported...) (Score: %.2f)" % (str(device_idx), _score))
_sonde_type = "C34C50"
elif 'MK2LMS' in _type:
logging.debug("Scanner #%s - Detected a 1680 MHz LMS6 Sonde (MK2A Telemetry)! (Score: %.2f, Offset: %.1f Hz)" % (str(device_idx), _score, _offset_est))

Wyświetl plik

@ -27,6 +27,7 @@ gcc lms6mod.c demod_mod.o bch_ecc_mod.o -lm -o lms6mod -w
gcc lms6Xmod.c demod_mod.o bch_ecc_mod.o -lm -o lms6Xmod -w
gcc meisei100mod.c demod_mod.o bch_ecc_mod.o -lm -o meisei100mod -w
gcc m10mod.c demod_mod.o -lm -o m10mod -w
gcc mXXmod.c demod_mod.o -lm -o mXXmod -w
# Build LMS6-1680 Decoder
echo "Building LMS6-1680 Demodulator."

Wyświetl plik

@ -20,9 +20,43 @@ alternative decoders using cross-correlation for better header-synchronization
`gcc rs92mod.c demod_mod.o bch_ecc_mod.o -lm -o rs92mod` (needs `RS/rs92/nav_gps_vel.c`)
#### Usage/Examples
`./rs41mod --ecc2 --crc -vx --ptu <audio.wav>` <br />
`./dfm09mod --ecc -v --ptu <audio.wav>` (add `-i` for dfm06)<br />
`./rs41mod --ecc2 -vx --ptu <audio.wav>` <br />
`./dfm09mod --ecc -v --ptu <audio.wav>` (add `-i` for dfm06; or use `--auto`) <br />
`./m10mod --dc -vv --ptu -c <audio.wav>` <br />
`./lms6Xmod --vit --ecc -v <audio.wav>` <br />
IQ data:<br />
If the IQ data is downsampled and centered (IF band), use <br />
`./rs41mod --iq2 <iq_data.wav>` <br />
or with lowpass filter <br />
`./rs41mod --iq2 --lp <iq_data.wav>` <br />
For baseband IQ data, use
`./rs41mod --IQ <fq> <iq_data.wav>` <br />
where `<fq>` is the relative frequency in `-0.5 .. 0.5`;
e.g. if the receiver is tuned to 403MHz and the (complex) sample rate is 2MHz,
a signal at 402.5MHz would be -0.5MHz off, i.e. `<fq> = -0.5/2 = -0.25`. <br />
For IQ data (i.e. 2 channels) it is possible to read raw data (without wav header): <br />
`./rs41mod --IQ <fq> - <sr> <bs> <iq_data.raw>` <br />
&nbsp;&nbsp;&nbsp;&nbsp; `<sr>`: sample rate <br />
&nbsp;&nbsp;&nbsp;&nbsp; `<bs>=8,16,32`: bits per (real) sample (u8, s16 or f32)
#### Remarks
FM-demodulation is sensitive to noise at higher frequencies. A narrow low-pass filter is needed before demodulation.
For weak signals and higher modulation indices IQ-decoding is usually better.
<br />
DFM:<br />
The high modulation index has advantages in IQ-decoding. <br />
`--ecc2` uses soft decision for 2-error words. If weak signals frequently produce errors, it is likely that
more than 2 errors occur in a received word. Since there is no additional frame protection (e.g. CRC), the
frames will not be decoded reliably in weak conditions. The `--dist` option has a thredshold for the number
of errors per packet.
<br />
LMS6-403:<br />
`lms6Xmod_soft.c` (testing) provides a soft viterbi decoding option `--vit2`;
IQ-decoding is recommended for soft decoding (noisy/spikey FM-signals don't always help soft decision).
The difference between hard and soft viterbi becomes only apparent at lower SNR. The inner convolutional
code does most of the error correction. The concatenated outer Reed-Solomon code kicks in only at low SNR.

Wyświetl plik

@ -42,35 +42,35 @@ typedef struct {
} RS_t;
static GF_t GF256RS = { f : 0x11D, // RS-GF(2^8): X^8 + X^4 + X^3 + X^2 + 1 : 0x11D
ord : 256, // 2^8
alpha: 0x02, // generator: alpha = X
exp_a: {0},
log_a: {0} };
static GF_t GF256RS = { .f = 0x11D, // RS-GF(2^8): X^8 + X^4 + X^3 + X^2 + 1 : 0x11D
.ord = 256, // 2^8
.alpha = 0x02, // generator: alpha = X
.exp_a = {0},
.log_a = {0} };
static GF_t GF256RSccsds = { f : 0x187, // RS-GF(2^8): X^8 + X^7 + X^2 + X + 1 : 0x187
ord : 256, // 2^8
alpha: 0x02, // generator: alpha = X
exp_a: {0},
log_a: {0} };
static GF_t GF256RSccsds = { .f = 0x187, // RS-GF(2^8): X^8 + X^7 + X^2 + X + 1 : 0x187
.ord = 256, // 2^8
.alpha = 0x02, // generator: alpha = X
.exp_a = {0},
.log_a = {0} };
static GF_t GF64BCH = { f : 0x43, // BCH-GF(2^6): X^6 + X + 1 : 0x43
ord : 64, // 2^6
alpha: 0x02, // generator: alpha = X
exp_a: {0},
log_a: {0} };
static GF_t GF64BCH = { .f = 0x43, // BCH-GF(2^6): X^6 + X + 1 : 0x43
.ord = 64, // 2^6
.alpha = 0x02, // generator: alpha = X
.exp_a = {0},
.log_a = {0} };
static GF_t GF16RS = { f : 0x13, // RS-GF(2^4): X^4 + X + 1 : 0x13
ord : 16, // 2^4
alpha: 0x02, // generator: alpha = X
exp_a: {0},
log_a: {0} };
static GF_t GF16RS = { .f = 0x13, // RS-GF(2^4): X^4 + X + 1 : 0x13
.ord = 16, // 2^4
.alpha = 0x02, // generator: alpha = X
.exp_a = {0},
.log_a = {0} };
static GF_t GF256AES = { f : 0x11B, // AES-GF(2^8): X^8 + X^4 + X^3 + X + 1 : 0x11B
ord : 256, // 2^8
alpha: 0x03, // generator: alpha = X+1
exp_a: {0},
log_a: {0} };
static GF_t GF256AES = { .f = 0x11B, // AES-GF(2^8): X^8 + X^4 + X^3 + X + 1 : 0x11B
.ord = 256, // 2^8
.alpha = 0x03, // generator: alpha = X+1
.exp_a = {0},
.log_a = {0} };
static RS_t RS256 = { 255, 12, 24, 231, 0, 1, 1, {0}, {0} };

Wyświetl plik

@ -3,6 +3,10 @@
* sync header: correlation/matched filter
* compile:
* gcc -c demod_mod.c
* speedup:
* gcc -O2 -c demod_mod.c
* or
* gcc -Ofast -c demod_mod.c
*
* author: zilog80
*/
@ -509,7 +513,7 @@ static int lowpass_init(float f, int taps, float **pws) {
h = (double*)calloc( taps+1, sizeof(double)); if (h == NULL) return -1;
w = (double*)calloc( taps+1, sizeof(double)); if (w == NULL) return -1;
ws = (float*)calloc( taps+1, sizeof(float)); if (ws == NULL) return -1;
ws = (float*)calloc( 2*taps+1, sizeof(float)); if (ws == NULL) return -1;
for (n = 0; n < taps; n++) {
w[n] = 7938/18608.0 - 9240/18608.0*cos(2*M_PI*n/(taps-1)) + 1430/18608.0*cos(4*M_PI*n/(taps-1)); // Blackmann
@ -520,6 +524,9 @@ static int lowpass_init(float f, int taps, float **pws) {
for (n = 0; n < taps; n++) {
ws[n] /= norm; // 1-norm
}
for (n = 0; n < taps; n++) ws[taps+n] = ws[n]; // duplicate/unwrap
*pws = ws;
free(h); h = NULL;
@ -551,13 +558,15 @@ static int lowpass_update(float f, int taps, float *ws) {
ws[n] /= norm; // 1-norm
}
for (n = 0; n < taps; n++) ws[taps+n] = ws[n];
free(h); h = NULL;
free(w); w = NULL;
return taps;
}
static float complex lowpass(float complex buffer[], ui32_t sample, ui32_t taps, float *ws) {
static float complex lowpass0(float complex buffer[], ui32_t sample, ui32_t taps, float *ws) {
ui32_t n;
double complex w = 0;
for (n = 0; n < taps; n++) {
@ -565,8 +574,18 @@ static float complex lowpass(float complex buffer[], ui32_t sample, ui32_t taps,
}
return (float complex)w;
}
static float complex lowpass(float complex buffer[], ui32_t sample, ui32_t taps, float *ws) {
ui32_t n;
ui32_t s = sample % taps;
double complex w = 0;
for (n = 0; n < taps; n++) {
w += buffer[n]*ws[taps+s-n]; // ws[taps+s-n] = ws[(taps+sample-n)%taps]
}
return (float complex)w;
// symmetry: ws[n] == ws[taps-1-n]
}
static float re_lowpass(float buffer[], ui32_t sample, ui32_t taps, float *ws) {
static float re_lowpass0(float buffer[], ui32_t sample, ui32_t taps, float *ws) {
ui32_t n;
double w = 0;
for (n = 0; n < taps; n++) {
@ -574,6 +593,15 @@ static float re_lowpass(float buffer[], ui32_t sample, ui32_t taps, float *ws) {
}
return (float)w;
}
static float re_lowpass(float buffer[], ui32_t sample, ui32_t taps, float *ws) {
ui32_t n;
ui32_t s = sample % taps;
double w = 0;
for (n = 0; n < taps; n++) {
w += buffer[n]*ws[taps+s-n]; // ws[taps+s-n] = ws[(taps+sample-n)%taps]
}
return (float)w;
}
int f32buf_sample(dsp_t *dsp, int inv) {
@ -807,7 +835,7 @@ int read_slbit(dsp_t *dsp, int *bit, int inv, int ofs, int pos, float l, int spi
}
sample -= dc;
if ( l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) sum -= sample;
if (l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) sum -= sample;
dsp->sc++;
} while (dsp->sc < bg); // n < dsp->sps
@ -825,9 +853,9 @@ int read_slbit(dsp_t *dsp, int *bit, int inv, int ofs, int pos, float l, int spi
+dsp->bufs[(dsp->sample_out-dsp->buffered+1 + ofs + dsp->M) % dsp->M]);
sample = avg + scale*(sample - avg); // spikes
}
sample -= dc;
sample -= dc;
if ( l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) sum += sample;
if (l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) sum += sample;
dsp->sc++;
} while (dsp->sc < bg); // n < dsp->sps
@ -839,6 +867,81 @@ int read_slbit(dsp_t *dsp, int *bit, int inv, int ofs, int pos, float l, int spi
return 0;
}
int read_softbit(dsp_t *dsp, hsbit_t *shb, int inv, int ofs, int pos, float l, int spike) {
// symlen==2: manchester2 10->0,01->1: 2.bit
float sample;
float avg;
float ths = 0.5, scale = 0.27;
double sum = 0.0;
double mid;
//double l = 1.0;
double bg = pos*dsp->symlen*dsp->sps;
double dc = 0.0;
ui8_t bit = 0;
if (dsp->opt_dc && dsp->opt_iq < 2) dc = dsp->dc;
if (pos == 0) {
bg = 0;
dsp->sc = 0;
}
if (dsp->symlen == 2) {
mid = bg + (dsp->sps-1)/2.0;
bg += dsp->sps;
do {
if (dsp->buffered > 0) dsp->buffered -= 1;
else if (f32buf_sample(dsp, inv) == EOF) return EOF;
sample = dsp->bufs[(dsp->sample_out-dsp->buffered + ofs + dsp->M) % dsp->M];
if (spike && fabs(sample - avg) > ths) {
avg = 0.5*(dsp->bufs[(dsp->sample_out-dsp->buffered-1 + ofs + dsp->M) % dsp->M]
+dsp->bufs[(dsp->sample_out-dsp->buffered+1 + ofs + dsp->M) % dsp->M]);
sample = avg + scale*(sample - avg); // spikes
}
sample -= dc;
if (l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) sum -= sample;
dsp->sc++;
} while (dsp->sc < bg); // n < dsp->sps
}
mid = bg + (dsp->sps-1)/2.0;
bg += dsp->sps;
do {
if (dsp->buffered > 0) dsp->buffered -= 1;
else if (f32buf_sample(dsp, inv) == EOF) return EOF;
sample = dsp->bufs[(dsp->sample_out-dsp->buffered + ofs + dsp->M) % dsp->M];
if (spike && fabs(sample - avg) > ths) {
avg = 0.5*(dsp->bufs[(dsp->sample_out-dsp->buffered-1 + ofs + dsp->M) % dsp->M]
+dsp->bufs[(dsp->sample_out-dsp->buffered+1 + ofs + dsp->M) % dsp->M]);
sample = avg + scale*(sample - avg); // spikes
}
sample -= dc;
if (l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) sum += sample;
dsp->sc++;
} while (dsp->sc < bg); // n < dsp->sps
if (sum >= 0) bit = 1;
else bit = 0;
shb->hb = bit;
shb->sb = (float)sum;
return 0;
}
/* -------------------------------------------------------------------------- */
#define IF_SAMPLE_RATE 48000

Wyświetl plik

@ -3,6 +3,11 @@
#include <math.h>
#include <complex.h>
#ifndef M_PI
#define M_PI (3.1415926535897932384626433832795)
#endif
typedef unsigned char ui8_t;
typedef unsigned short ui16_t;
typedef unsigned int ui32_t;
@ -133,10 +138,17 @@ typedef struct {
} pcm_t;
typedef struct {
ui8_t hb;
float sb;
} hsbit_t;
float read_wav_header(pcm_t *, FILE *);
int f32buf_sample(dsp_t *, int);
int read_slbit(dsp_t *, int*, int, int, int, float, int);
int read_softbit(dsp_t *, hsbit_t *, int, int, int, float, int );
int init_buffers(dsp_t *);
int free_buffers(dsp_t *);

Wyświetl plik

@ -68,7 +68,7 @@ typedef struct {
float status[2];
float _frmcnt;
char sonde_id[16]; // "ID__:xxxxxxxx\0\0"
char frame_bits[BITFRAME_LEN+4];
hsbit_t frame[BITFRAME_LEN+4]; // char frame_bits[BITFRAME_LEN+4];
char dat_str[9][13+1];
sn_t snc;
pcksts_t pck[9];
@ -98,6 +98,15 @@ static char dfm_header[] = "0100010111001111";
#define DAT2 (16+160) // 104 bit
// frame: 280 bit
static ui8_t G[8][4] = // Generator
{{ 1, 0, 0, 0},
{ 0, 1, 0, 0},
{ 0, 0, 1, 0},
{ 0, 0, 0, 1},
{ 0, 1, 1, 1},
{ 1, 0, 1, 1},
{ 1, 1, 0, 1},
{ 1, 1, 1, 0}};
static ui8_t H[4][8] = // Parity-Check
{{ 0, 1, 1, 1, 1, 0, 0, 0},
{ 1, 0, 1, 1, 0, 1, 0, 0},
@ -105,6 +114,28 @@ static ui8_t H[4][8] = // Parity-Check
{ 1, 1, 1, 0, 0, 0, 0, 1}};
static ui8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H:
// 1-bit-error-Syndrome
static ui8_t codewords[16][8]; // (valid) Hamming codewords
static int nib4bits(ui8_t nib, ui8_t *bits) { // big endian
int j;
nib &= 0xF;
for (j = 0; j < 4; j++) {
bits[j] = (nib>>(3-j)) & 0x1;
}
return 0;
}
static int gencode(ui8_t msg[4], ui8_t code[8]) {
int i, j; // Gm=c
for (i = 0; i < 8; i++) {
code[i] = 0;
for (j = 0; j < 4; j++) {
code[i] ^= G[i][j] & msg[j];
}
}
return 0;
}
static ui32_t bits2val(ui8_t *bits, int len) { // big endian
int j;
@ -117,18 +148,16 @@ static ui32_t bits2val(ui8_t *bits, int len) { // big endian
return val;
}
static void deinterleave(char *str, int L, ui8_t *block) {
static void deinterleave(hsbit_t *str, int L, hsbit_t *block) {
int i, j;
for (j = 0; j < B; j++) { // L = 7, 13
for (i = 0; i < L; i++) {
if (str[L*j+i] >= 0x30 && str[L*j+i] <= 0x31) {
block[B*i+j] = str[L*j+i] - 0x30; // ASCII -> bit
}
block[B*i+j] = str[L*j+i];
}
}
}
static int check(ui8_t code[8]) {
static int check(int opt_ecc, hsbit_t code[8]) {
int i, j; // Bei Demodulierung durch Nulldurchgaenge, wenn durch Fehler ausser Takt,
ui32_t synval = 0; // verschieben sich die bits. Fuer Hamming-Decode waere es besser,
ui8_t syndrom[4]; // sync zu Beginn mit Header und dann Takt beibehalten fuer decision.
@ -137,7 +166,7 @@ static int check(ui8_t code[8]) {
for (i = 0; i < 4; i++) { // S = 4
syndrom[i] = 0;
for (j = 0; j < 8; j++) { // B = 8
syndrom[i] ^= H[i][j] & code[j];
syndrom[i] ^= H[i][j] & code[j].hb;
}
}
synval = bits2val(syndrom, 4);
@ -151,22 +180,67 @@ static int check(ui8_t code[8]) {
}
}
else ret = 0;
if (ret > 0) code[ret-1] ^= 0x1;
if (ret > 0) code[ret-1].hb ^= 0x1; // d=1: 1-bit-error
else if (ret < 0 && opt_ecc == 2) { // d=2: 2-bit-error: soft decision
// Hamming(8,4)
// 256 words:
// 16 codewords
// 16*8=128 1-error words (dist=1)
// 16*7=112 2-error words (dist=2)
// each 2-error word has 4 codewords w/ dist=2,
// choose best match/correlation
int n;
int maxn = -1;
int d = 0;
float sum = 0.0;
float maxsum = 0.0;
/*
sum = 0.0; // s<0: h=0 , s>0: h=1
for (i = 0; i < 8; i++) { // h\in{0,1} -> 2h-1\in{-1,+1}
sum += (2*code[i].hb-1)*code[i].sb; // i.e. sum_i |s_i|
} // original score
*/
for (n = 0; n < 16; n++) {
d = 0;
for (i = 0; i < 8; i++) { // d(a,b) = sum_i a[i]^b[i]
if (code[i].hb != codewords[n][i]) d++;
}
if (d == 2) { // check dist=2 codewords - in principle, all codewords could be tested
// softbits correlation:
// - interleaving
// + no pulse-shaping -> sum
sum = 0.0;
for (i = 0; i < 8; i++) {
sum += (2*codewords[n][i]-1) * code[i].sb;
}
if (sum >= maxsum) { // best match
maxsum = sum;
maxn = n;
}
}
}
if (maxn >= 0) {
for (i = 0; i < 8; i++) {
if (code[i].hb = codewords[maxn][i]);
}
}
}
return ret;
}
static int hamming(int opt_ecc, ui8_t *ham, int L, ui8_t *sym) {
static int hamming(int opt_ecc, hsbit_t *ham, int L, ui8_t *sym) {
int i, j;
int ecc = 0, ret = 0; // L = 7, 13
for (i = 0; i < L; i++) { // L * 2 nibble (data+parity)
if (opt_ecc) {
ecc = check(ham+B*i);
ecc = check(opt_ecc, ham+B*i);
if (ecc > 0) ret |= (1<<i);
if (ecc < 0) ret |= ecc; // -1
}
for (j = 0; j < S; j++) { // systematic: bits 0..S-1 data
sym[S*i+j] = ham[B*i+j];
sym[S*i+j] = ham[B*i+j].hb;
}
}
return ret;
@ -561,6 +635,11 @@ static int conf_out(gpx_t *gpx, ui8_t *conf_bits, int ec) {
gpx->status[1] = val/100.0;
}
}
else {
gpx->status[0] = 0;
gpx->status[1] = 0;
}
return ret;
}
@ -668,8 +747,12 @@ static void print_gpx(gpx_t *gpx) {
}
// Print JSON blob // valid sonde_ID?
printf("{ \"frame\": %d, \"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f, \"batt\": %.2f",
gpx->frnr, json_sonde_id, gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek, gpx->lat, gpx->lon, gpx->alt, gpx->horiV, gpx->dir, gpx->vertV, gpx->status[0]);
printf("{ \"type\": \"%s\"", "DFM");
printf(", \"frame\": %d, \"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f",
gpx->frnr, json_sonde_id, gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek, gpx->lat, gpx->lon, gpx->alt, gpx->horiV, gpx->dir, gpx->vertV);
if (gpx->ptu_out >= 0xA && gpx->status[0] > 0) { // DFM>=09(P): Battery (STM32)
printf(", \"batt\": %.2f", gpx->status[0]);
}
if (gpx->ptu_out) { // get temperature
float t = get_Temp(gpx); // ecc-valid temperature?
if (t > -270.0) printf(", \"temp\": %.1f", t);
@ -691,17 +774,17 @@ static int print_frame(gpx_t *gpx) {
int ret0, ret1, ret2;
int ret = 0;
ui8_t hamming_conf[ 7*B]; // 7*8=56
ui8_t hamming_dat1[13*B]; // 13*8=104
ui8_t hamming_dat2[13*B];
hsbit_t hamming_conf[ 7*B]; // 7*8=56
hsbit_t hamming_dat1[13*B]; // 13*8=104
hsbit_t hamming_dat2[13*B];
ui8_t block_conf[ 7*S]; // 7*4=28
ui8_t block_dat1[13*S]; // 13*4=52
ui8_t block_dat2[13*S];
deinterleave(gpx->frame_bits+CONF, 7, hamming_conf);
deinterleave(gpx->frame_bits+DAT1, 13, hamming_dat1);
deinterleave(gpx->frame_bits+DAT2, 13, hamming_dat2);
deinterleave(gpx->frame+CONF, 7, hamming_conf);
deinterleave(gpx->frame+DAT1, 13, hamming_dat1);
deinterleave(gpx->frame+DAT2, 13, hamming_dat2);
ret0 = hamming(gpx->option.ecc, hamming_conf, 7, block_conf);
ret1 = hamming(gpx->option.ecc, hamming_dat1, 13, block_dat1);
@ -750,14 +833,14 @@ static int print_frame(gpx_t *gpx) {
}
else if (gpx->option.ecc) {
if (ret0 == 0 || ret0 > 0) {
if (ret0 == 0 || ret0 > 0 || gpx->option.ecc == 2) {
conf_out(gpx, block_conf, ret0);
}
if (ret1 == 0 || ret1 > 0) {
if (ret1 == 0 || ret1 > 0 || gpx->option.ecc == 2) {
frid = dat_out(gpx, block_dat1, ret1);
if (frid == 8) print_gpx(gpx);
}
if (ret2 == 0 || ret2 > 0) {
if (ret2 == 0 || ret2 > 0 || gpx->option.ecc == 2) {
frid = dat_out(gpx, block_dat2, ret2);
if (frid == 8) print_gpx(gpx);
}
@ -866,7 +949,7 @@ int main(int argc, char **argv) {
int ret = 0;
int k;
int bit;
hsbit_t hsbit;
int bitpos = 0;
int bitQ;
int pos;
@ -926,7 +1009,8 @@ int main(int argc, char **argv) {
else if ( (strcmp(*argv, "-i") == 0) || (strcmp(*argv, "--invert") == 0) ) {
option_inv = 0x1;
}
else if ( (strcmp(*argv, "--ecc") == 0) ) { option_ecc = 1; }
else if ( (strcmp(*argv, "--ecc" ) == 0) ) { option_ecc = 1; }
else if ( (strcmp(*argv, "--ecc2") == 0) ) { option_ecc = 2; }
else if ( (strcmp(*argv, "--ptu") == 0) ) {
option_ptu = 1;
//gpx.ptu_out = 1; // force ptu (non PS-15)
@ -1002,9 +1086,31 @@ int main(int argc, char **argv) {
}
if (!wavloaded) fp = stdin;
// ecc2-soft_decision accepts also 2-error words,
// so the probability for 3 errors is high and will
// produce wrong codewords. hence ecc2 is not recommended
// for reliable frame decoding.
//
if ( option_dist || option_json ) option_ecc = 1;
if (option_ecc)
{
ui8_t nib, msg[4], code[8];
for (nib = 0; nib < 16; nib++) {
nib4bits(nib, msg);
gencode(msg, code);
for (k = 0; k < 8; k++) codewords[nib][k] = code[k];
}
}
// init gpx
strcpy(gpx.frame_bits, dfm_header); //, sizeof(dfm_header);
//strcpy(gpx.frame_bits, dfm_header); //, sizeof(dfm_header);
for (k = 0; k < strlen(dfm_header); k++) {
gpx.frame[k].hb = dfm_header[k] & 1;
gpx.frame[k].sb = 2*gpx.frame[k].hb - 1;
}
for (k = 0; k < 9; k++) gpx.pck[k].ec = -1; // init ecc-status
gpx.option.inv = option_inv;
@ -1093,89 +1199,96 @@ int main(int argc, char **argv) {
bitofs += shift;
while ( 1 )
while ( 1 )
{
if (option_bin) { // aka find_binrawhead()
header_found = find_binhead(fp, &hdb, &_mv); // symbols or bits?
hdrcnt += nfrms;
}
else { // FM-audio:
header_found = find_header(&dsp, thres, 2, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0
_mv = dsp.mv;
}
if (header_found == EOF) break;
// mv == correlation score
if (_mv *(0.5-gpx.option.inv) < 0) {
if (gpx.option.aut == 0) header_found = 0;
else gpx.option.inv ^= 0x1;
}
if (header_found)
{
if (option_bin) { // aka find_binrawhead()
header_found = find_binhead(fp, &hdb, &_mv); // symbols or bits?
hdrcnt += nfrms;
}
else { // FM-audio:
header_found = find_header(&dsp, thres, 2, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0
_mv = dsp.mv;
}
if (header_found == EOF) break;
bitpos = 0;
pos = headerlen;
pos /= 2;
// mv == correlation score
if (_mv *(0.5-gpx.option.inv) < 0) {
if (gpx.option.aut == 0) header_found = 0;
else gpx.option.inv ^= 0x1;
}
//if (fabs(mv) > 0.85) nfrms = 8; else nfrms = 4; // test OK/KO/NO count
if (header_found)
{
bitpos = 0;
pos = headerlen;
pos /= 2;
//if (fabs(mv) > 0.85) nfrms = 8; else nfrms = 4; // test OK/KO/NO count
frm = 0;
while ( frm < nfrms ) { // nfrms=1,2,4,8
frm = 0;
while ( frm < nfrms ) { // nfrms=1,2,4,8
if (option_bin) {
gpx._frmcnt = hdrcnt + frm;
}
else {
gpx._frmcnt = dsp.mv_pos/(2.0*dsp.sps*BITFRAME_LEN) + frm;
}
while ( pos < BITFRAME_LEN )
{
if (option_bin) {
gpx._frmcnt = hdrcnt + frm;
// symbols or bits?
// manchester1 1->10,0->01: 1.bit (DFM-06)
// manchester2 0->10,1->01: 2.bit (DFM-09)
bitQ = fgetc(fp);
if (bitQ != EOF) {
hsbit.hb = bitQ & 0x1;
bitQ = fgetc(fp); // check: rbit0^rbit1=1 (Manchester)
if (bitQ != EOF) hsbit.hb = bitQ & 0x1; // 2.bit (DFM-09)
hsbit.sb = 2*hsbit.hb - 1;
}
}
else {
gpx._frmcnt = dsp.mv_pos/(2.0*dsp.sps*BITFRAME_LEN) + frm;
}
while ( pos < BITFRAME_LEN )
{
if (option_bin) {
// symbols or bits?
// manchester1 1->10,0->01: 1.bit (DFM-06)
// manchester2 0->10,1->01: 2.bit (DFM-09)
bitQ = fgetc(fp);
if (bitQ != EOF) {
bit = bitQ & 0x1;
bitQ = fgetc(fp); // check: rbit0^rbit1=1 (Manchester)
if (bitQ != EOF) bit = bitQ & 0x1; // 2.bit (DFM-09)
}
if (option_iq >= 2) {
float bl = -1;
if (option_iq > 2) bl = 4.0;
bitQ = read_softbit(&dsp, &hsbit, 0, bitofs, bitpos, bl, 0);
}
else {
if (option_iq >= 2) {
float bl = -1;
if (option_iq > 2) bl = 4.0;
bitQ = read_slbit(&dsp, &bit, 0/*gpx.option.inv*/, bitofs, bitpos, bl, 0);
}
else {
bitQ = read_slbit(&dsp, &bit, 0/*gpx.option.inv*/, bitofs, bitpos, -1, spike);
}
bitQ = read_softbit(&dsp, &hsbit, 0, bitofs, bitpos, -1, spike);
}
if ( bitQ == EOF ) { frm = nfrms; break; } // liest 2x EOF
// optional:
// normalize soft bit s_j by
// rhsbit.sb /= dsp._spb+1; // all samples in [-1,+1]
if (gpx.option.inv) bit ^= 1;
gpx.frame_bits[pos] = 0x30 + bit;
pos++;
bitpos += 1;
}
gpx.frame_bits[pos] = '\0';
if ( bitQ == EOF ) { frm = nfrms; break; } // liest 2x EOF
ret = print_frame(&gpx);
if (pos < BITFRAME_LEN) break;
pos = 0;
frm += 1;
//if (ret < 0) frms += 1;
if (gpx.option.inv) {
hsbit.hb ^= 1;
hsbit.sb = -hsbit.sb;
}
gpx.frame[pos] = hsbit;
pos++;
bitpos += 1;
}
ret = print_frame(&gpx);
if (pos < BITFRAME_LEN) break;
pos = 0;
frm += 1;
//if (ret < 0) frms += 1;
}
header_found = 0;
pos = headerlen;
}
if (!option_bin) free_buffers(&dsp);
else {
if (hdb.buf) { free(hdb.buf); hdb.buf = NULL; }
}
header_found = 0;
pos = headerlen;
}
if (!option_bin) free_buffers(&dsp);
else {
if (hdb.buf) { free(hdb.buf); hdb.buf = NULL; }
}
fclose(fp);

Wyświetl plik

@ -738,11 +738,13 @@ static void print_frame(gpx_t *gpx, int crc_err, int len) {
// UTC oder GPS?
char sntyp[] = "LMS6-";
if (gpx->typ == 10) sntyp[3] = 'X';
printf("{ \"frame\": %d, \"id\": \"%s%d\", \"datetime\": \"", gpx->frnr, sntyp, gpx->sn );
printf("{ \"type\": \"%s\"", "LMS");
printf(", \"frame\": %d, \"id\": \"%s%d\", \"datetime\": \"", gpx->frnr, sntyp, gpx->sn );
//if (gpx->week > 0) printf("%04d-%02d-%02dT", gpx->jahr, gpx->monat, gpx->tag );
printf("%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f",
gpx->std, gpx->min, gpx->sek, gpx->lat, gpx->lon, gpx->alt, gpx->vH, gpx->vD, gpx->vV );
printf(", \"gpstow\": %d", gpx->gpstow );
printf(", \"subtype\": \"%c\"", sntyp[3]); // "6":LMS6-403, "X":lms6X, "MK2A":LMS6-1680/Mk2a
printf(" }\n");
printf("\n");
}

Wyświetl plik

@ -61,6 +61,7 @@ dduudduudduudduu duduudduuduudduu ddududuudduduudd uduuddududududud uudduduuddu
//"0111011010011111"; // M10: 76 9F , w/ aux-data
//"0110010001001001"; // M10-dop: 64 49 09
//"0110010010101111"; // M10+: 64 AF w/ gtop-GPS
//"0100010100100000"; // M20: 45 20 (baud=9600)
static char rawheader[] = "10011001100110010100110010011001";
#define FRAME_LEN (100+1) // 0x64+1
@ -70,6 +71,11 @@ static char rawheader[] = "10011001100110010100110010011001";
#define BITAUX_LEN (AUX_LEN*BITS)
#define t_M2K2 0x8F
#define t_M10 0x9F
#define t_M10plus 0xAF
#define t_M20 0x20
typedef struct {
int week; int tow_ms; int gpssec;
int jahr; int monat; int tag;
@ -78,6 +84,8 @@ typedef struct {
double lat; double lon; double alt;
double vH; double vD; double vV;
double vx; double vy; double vD2;
float T; float _RH;
float Ti; float batV;
ui8_t numSV;
ui8_t utc_ofs;
char SN[12];
@ -85,7 +93,7 @@ typedef struct {
char frame_bits[BITFRAME_LEN+BITAUX_LEN+8];
int auxlen; // 0 .. 0x76-0x64
option_t option;
double bLevel;
ui8_t type;
} gpx_t;
@ -201,6 +209,7 @@ M10 w/ Sierra Wireless Airprime X1110
#define stdFLEN 0x64 // pos[0]=0x64
// Trimble GPS
#define pos_GPSTOW 0x0A // 4 byte
#define pos_GPSlat 0x0E // 4 byte
#define pos_GPSlon 0x12 // 4 byte
@ -212,9 +221,21 @@ M10 w/ Sierra Wireless Airprime X1110
#define pos_GPSvE 0x04 // 2 byte
#define pos_GPSvN 0x06 // 2 byte
#define pos_GPSvU 0x08 // 2 byte
#define pos_SN 0x5D // 2+3 byte
#define pos_CNT 0x62 // 1 byte
#define pos_Check (stdFLEN-1) // 2 byte
// Gtop GPS
#define pos_gtopGPSlat 0x04 // 4 byte
#define pos_gtopGPSlon 0x08 // 4 byte
#define pos_gtopGPSalt 0x0C // 3 byte
#define pos_gtopGPSvE 0x0F // 2 byte
#define pos_gtopGPSvN 0x11 // 2 byte
#define pos_gtopGPSvU 0x13 // 2 byte
#define pos_gtopGPStime 0x15 // 3 byte
#define pos_gtopGPSdate 0x18 // 3 byte
#define ANSI_COLOR_RED "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
@ -226,6 +247,7 @@ M10 w/ Sierra Wireless Airprime X1110
#define XTERM_COLOR_BROWN "\x1b[38;5;94m" // 38;5;{0..255}m
#define col_Mtype "\x1b[38;5;250m" // 1 byte
#define col_GPSweek "\x1b[38;5;20m" // 2 byte
#define col_GPSTOW "\x1b[38;5;27m" // 4 byte
#define col_GPSdate "\x1b[38;5;94m" //111
@ -234,11 +256,12 @@ M10 w/ Sierra Wireless Airprime X1110
#define col_GPSalt "\x1b[38;5;82m" // 4 byte
#define col_GPSvel "\x1b[38;5;36m" // 6 byte
#define col_SN "\x1b[38;5;58m" // 3 byte
#define col_CNT "\x1b[38;5;172m" // 1 byte
#define col_Check "\x1b[38;5;11m" // 2 byte
#define col_TXT "\x1b[38;5;244m"
#define col_FRTXT "\x1b[38;5;244m"
#define col_CSok "\x1b[38;5;2m"
#define col_CSno "\x1b[38;5;1m"
#define col_TXT "\x1b[38;5;244m"
#define col_FRTXT "\x1b[38;5;244m"
/*
$ for code in {0..255}
@ -443,19 +466,91 @@ static int get_SN(gpx_t *gpx) {
return 0;
}
// Battery Voltage
static int get_BatteryLevel(gpx_t *gpx) {
/* -------------------------------------------------------------------------- */
//
// M10+ w/ Gtop
double batteryLevel = 0.0;
static int get_gtopGPSpos(gpx_t *gpx) {
int i;
ui8_t bytes[4];
int val;
unsigned short batLvl;
batLvl = (gpx->frame_bytes[70] << 8) | gpx->frame_bytes[69];
// Thanks F5MVO for the formula !
batteryLevel = (double)batLvl/1000.*6.62;
for (i = 0; i < 4; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPSlat + i];
val = 0;
for (i = 0; i < 4; i++) val |= bytes[i] << (8*(3-i));
gpx->lat = val/1e6;
gpx->bLevel = batteryLevel ;
for (i = 0; i < 4; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPSlon + i];
val = 0;
for (i = 0; i < 4; i++) val |= bytes[i] << (8*(3-i));
gpx->lon = val/1e6;
for (i = 0; i < 3; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPSalt + i];
val = 0;
for (i = 0; i < 3; i++) val |= bytes[i] << (8*(2-i));
if (val & 0x800000) val -= 0x1000000; // alt: signed 24bit?
gpx->alt = val/1e2;
return 0;
}
static int get_gtopGPSvel(gpx_t *gpx) {
int i;
ui8_t bytes[2];
short vel16;
double vx, vy, vz, dir;
for (i = 0; i < 2; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPSvE + i];
vel16 = bytes[0] << 8 | bytes[1];
vx = vel16 / 1e2; // east
for (i = 0; i < 2; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPSvN + i];
vel16 = bytes[0] << 8 | bytes[1];
vy= vel16 / 1e2; // north
for (i = 0; i < 2; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPSvU + i];
vel16 = bytes[0] << 8 | bytes[1];
vz = vel16 / 1e2; // up
gpx->vx = vx;
gpx->vy = vy;
gpx->vH = sqrt(vx*vx+vy*vy);
dir = atan2(vx, vy) * 180 / M_PI;
if (dir < 0) dir += 360;
gpx->vD = dir;
gpx->vV = vz;
return 0;
}
static int get_gtopGPStime(gpx_t *gpx) {
int i;
ui8_t bytes[4];
int time;
for (i = 0; i < 3; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPStime + i];
time = 0;
for (i = 0; i < 3; i++) time |= bytes[i] << (8*(2-i));
gpx->std = time/10000;
gpx->min = (time%10000)/100;
gpx->sek = (time%100)/1.0;
return 0;
}
static int get_gtopGPSdate(gpx_t *gpx) {
int i;
ui8_t bytes[4];
int date;
for (i = 0; i < 3; i++) bytes[i] = gpx->frame_bytes[pos_gtopGPSdate + i];
date = 0;
for (i = 0; i < 3; i++) date |= bytes[i] << (8*(2-i));
gpx->jahr = 2000 + date%100;
gpx->monat = (date%10000)/100;
gpx->tag = date/10000;
return 0;
}
@ -526,7 +621,7 @@ static int checkM10(ui8_t *msg, int len) {
// Temperatur Sensor
// NTC-Thermistor Shibaura PB5-41E
//
static float get_Temp(gpx_t *gpx, int csOK) {
static float get_Temp(gpx_t *gpx) {
// NTC-Thermistor Shibaura PB5-41E
// T00 = 273.15 + 0.0 , R00 = 15e3
// T25 = 273.15 + 25.0 , R25 = 5.369e3
@ -560,7 +655,7 @@ static float get_Temp(gpx_t *gpx, int csOK) {
// range/scale 0, 1, 2: // M10-pcb
float Rs[3] = { 12.1e3 , 36.5e3 , 475.0e3 }; // bias/series
float Rp[3] = { 1e20 , 330.0e3 , 3000.0e3 }; // parallel, Rp[0]=inf
float Rp[3] = { 1e20 , 330.0e3 , 2000.0e3 }; // parallel, Rp[0]=inf
ui8_t scT; // {0,1,2}, range/scale voltage divider
ui16_t ADC_RT; // ADC12 P6.7(A7) , adr_0377h,adr_0376h
@ -582,22 +677,23 @@ static float get_Temp(gpx_t *gpx, int csOK) {
if (R > 0) T = 1/( p0 + p1*log(R) + p2*log(R)*log(R) + p3*log(R)*log(R)*log(R) );
if (gpx->option.vbs >= 3 && csOK) { // on-chip temperature
ui16_t ADC_Ti_raw = (gpx->frame_bytes[0x49] << 8) | gpx->frame_bytes[0x48]; // int.temp.diode, ref: 4095->1.5V
float vti, ti;
// INCH1A (temp.diode), slau144
vti = ADC_Ti_raw/4095.0 * 1.5; // V_REF+ = 1.5V, no calibration
ti = (vti-0.986)/0.00355; // 0.986/0.00355=277.75, 1.5/4095/0.00355=0.1032
fprintf(stdout, " (Ti:%.1fC)", ti);
// SegmentA-Calibration:
//ui16_t T30 = adr_10e2h; // CAL_ADC_15T30
//ui16_t T85 = adr_10e4h; // CAL_ADC_15T85
//float tic = (ADC_Ti_raw-T30)*(85.0-30.0)/(T85-T30) + 30.0;
//fprintf(stdout, " (Tic:%.1fC)", tic);
}
return T - 273.15; // Celsius
}
static float get_intTemp(gpx_t *gpx) {
// on-chip temperature
ui16_t ADC_Ti_raw = (gpx->frame_bytes[0x49] << 8) | gpx->frame_bytes[0x48]; // int.temp.diode, ref: 4095->1.5V
float vti, ti;
// INCH1A (temp.diode), slau144
vti = ADC_Ti_raw/4095.0 * 1.5; // V_REF+ = 1.5V, no calibration
ti = (vti-0.986)/0.00355; // 0.986/0.00355=277.75, 1.5/4095/0.00355=0.1032
gpx->Ti = ti;
// SegmentA-Calibration:
//ui16_t T30 = adr_10e2h; // CAL_ADC_15T30
//ui16_t T85 = adr_10e4h; // CAL_ADC_15T85
//float tic = (ADC_Ti_raw-T30)*(85.0-30.0)/(T85-T30) + 30.0;
}
/*
frame[0x32]: adr_1074h
frame[0x33]: adr_1075h
@ -640,7 +736,7 @@ frame[0x5F]: adr_1084h (SN)
frame[0x60]: adr_1080h (SN)
frame[0x61]: adr_1081h (SN)
*/
static float get_Tntc2(gpx_t *gpx, int csOK) {
static float get_Tntc2(gpx_t *gpx) {
// SMD ntc
float Rs = 22.1e3; // P5.6=Vcc
// float R25 = 2.2e3;
@ -654,14 +750,13 @@ static float get_Tntc2(gpx_t *gpx, int csOK) {
float T = 0.0; // T/Kelvin
ui16_t ADC_ntc2; // ADC12 P6.4(A4)
float x, R;
if (csOK)
{
ADC_ntc2 = (gpx->frame_bytes[0x5A] << 8) | gpx->frame_bytes[0x59];
x = (4095.0 - ADC_ntc2)/ADC_ntc2; // (Vcc-Vout)/Vout
R = Rs / x;
//if (R > 0) T = 1/(1/T25 + 1/b * log(R/R25));
if (R > 0) T = 1/( p0 + p1*log(R) + p2*log(R)*log(R) + p3*log(R)*log(R)*log(R) );
}
ADC_ntc2 = (gpx->frame_bytes[0x5A] << 8) | gpx->frame_bytes[0x59];
x = (4095.0 - ADC_ntc2)/ADC_ntc2; // (Vcc-Vout)/Vout
R = Rs / x;
//if (R > 0) T = 1/(1/T25 + 1/b * log(R/R25));
if (R > 0) T = 1/( p0 + p1*log(R) + p2*log(R)*log(R) + p3*log(R)*log(R)*log(R) );
return T - 273.15;
}
@ -672,7 +767,7 @@ static float get_Tntc2(gpx_t *gpx, int csOK) {
#define LN2 0.693147181
#define ADR_108A 1000.0 // 0x3E8=1000
float get_count_55(gpx_t *gpx) { // CalRef 55%RH , T=20C ?
static float get_count_55(gpx_t *gpx) { // CalRef 55%RH , T=20C ?
ui32_t TBCREF_1000 = gpx->frame_bytes[0x32] | (gpx->frame_bytes[0x33]<<8) | (gpx->frame_bytes[0x34]<<16);
return TBCREF_1000 / ADR_108A;
}
@ -681,26 +776,16 @@ static float get_count_RH(gpx_t *gpx) { // capture 1000 rising edges
ui32_t TBCCR1_1000 = gpx->frame_bytes[0x35] | (gpx->frame_bytes[0x36]<<8) | (gpx->frame_bytes[0x37]<<16);
return TBCCR1_1000 / ADR_108A;
}
static float get_TLC555freq(gpx_t *gpx, float count) {
static float get_TLC555freq(gpx_t *gpx) {
return FREQ_CAPCLK / get_count_RH(gpx);
}
float get_C_RH(float freq, float T) { // TLC555 astable: R_A=3.65k, R_B=338k
float R_B = 338e3;
float R_A = 3.65e3;
float td = 0;
float C_RH = (1/freq - 2*td) / (LN2 * (R_A + 2*R_B));
// freq/T compensation ...
return C_RH;
}
float cRHc55_RH(gpx_t *gpx, float cRHc55) { // C_RH / C_55
static float cRHc55_RH(gpx_t *gpx, float cRHc55) { // C_RH / C_55
// U.P.S.I.
// C_RH/C_55 = 0.8955 + 0.002*RH , T=20C
// C_RH = C_RH(RH,T) , RH = RH(C_RH,T)
// C_RH/C_55 approx.eq. count_RH/count_ref
float TH = get_Tntc2(gpx, 0);
float Tc = get_Temp(gpx, 0);
float TH = get_Tntc2(gpx);
float Tc = get_Temp(gpx);
float rh = (cRHc55-0.8955)/0.002; // UPSI linear transfer function
// temperature compensation
float T0 = 0.0, T1 = -30.0; // T/C
@ -712,17 +797,7 @@ float cRHc55_RH(gpx_t *gpx, float cRHc55) { // C_RH / C_55
return rh;
}
float get_RHc(gpx_t *gpx, int csOK) { // experimental/raw, errors~10%
float Tc = get_Temp(gpx, 0);
float count_ref = get_count_55(gpx); // CalRef 55%RH , T=20C ?
float count_RH = get_count_RH(gpx);
float C_55 = get_C_RH(get_TLC555freq(gpx, count_ref), 20.0); // CalRef 55%RH , T=20C ?
float C_RH = get_C_RH(get_TLC555freq(gpx, count_RH), Tc); // Tc == T_555 ?
float cRHc55 = C_RH / C_55;
return cRHc55_RH(gpx, cRHc55);
}
float get_RH(gpx_t *gpx, int csOK) { // experimental/raw, errors~10%
static float get_RH(gpx_t *gpx) {
//ui32_t TBCREF_1000 = frame_bytes[0x32] | (frame_bytes[0x33]<<8) | (frame_bytes[0x34]<<16); // CalRef 55%RH , T=20C ?
//ui32_t TBCCR1_1000 = frame_bytes[0x35] | (frame_bytes[0x36]<<8) | (frame_bytes[0x37]<<16); // FrqCnt TLC555
//float cRHc55 = TBCCR1_1000 / (float)TBCREF_1000; // CalRef 55%RH , T=20C ?
@ -730,28 +805,77 @@ float get_RH(gpx_t *gpx, int csOK) { // experimental/raw, errors~10%
return cRHc55_RH(gpx, cRHc55);
}
/*
static float get_C_RH() { // TLC555 astable: R_A=3.65k, R_B=338k
double R_B = 338e3;
double R_A = 3.65e3;
double C_RH = 1/get_TLC555freq() / (LN2 * (R_A + 2*R_B));
return C_RH;
}
*/
// Battery Voltage
// M10 batteries: 4xAA
static double get_BatV(gpx_t *gpx) {
float batV = 0.0;
ui32_t batADC = 0;
// ADC12_A5/4, V_R+=2.5V : 4096/4
// 0..1023 <-> 0V .. 2.5V
batADC = (gpx->frame_bytes[0x46] << 8) | gpx->frame_bytes[0x45];
// R1=[06D]=113kOhm
// R2=[30D]=200kOhm
// f=(R1+R2)/R2=2.77
//batV = 6.62*batADC/1000.0;
batV = 2.709 * batADC*2.5/1023.0;
//fprintf(stdout, " (bat0:%d/1023=%.2f)", batADC, batADC/1023.0);
return batV;
}
/* -------------------------------------------------------------------------- */
static int print_pos(gpx_t *gpx, int csOK) {
int err, err2;
err = 0;
err |= get_GPSweek(gpx);
err |= get_GPStime(gpx);
err |= get_GPSlat(gpx);
err |= get_GPSlon(gpx);
err |= get_GPSalt(gpx);
err2 = get_GPSvel(gpx);
err |= get_BatteryLevel(gpx);
if (gpx->type == t_M10) {
err |= get_GPSweek(gpx);
err |= get_GPStime(gpx);
err |= get_GPSlat(gpx);
err |= get_GPSlon(gpx);
err |= get_GPSalt(gpx);
err2 = get_GPSvel(gpx);
}
else if (gpx->type == t_M10plus) {
err |= get_gtopGPStime(gpx);
err |= get_gtopGPSdate(gpx);
err |= get_gtopGPSpos(gpx);
err2 = get_gtopGPSvel(gpx);
}
else err = 0xFF;
if (!err) {
Gps2Date(gpx->week, gpx->gpssec, &gpx->jahr, &gpx->monat, &gpx->tag);
if (gpx->type == t_M10) {
Gps2Date(gpx->week, gpx->gpssec, &gpx->jahr, &gpx->monat, &gpx->tag);
}
gpx->T = get_Temp(gpx);
gpx->_RH = get_RH(gpx);
gpx->Ti = get_intTemp(gpx);
gpx->batV = get_BatV(gpx);
if (gpx->option.col) {
fprintf(stdout, col_TXT);
if (gpx->option.vbs >= 3) fprintf(stdout, " (W "col_GPSweek"%d"col_TXT") ", gpx->week);
fprintf(stdout, col_GPSTOW"%s"col_TXT" ", weekday[gpx->wday]);
if (gpx->type == t_M10)
{
if (gpx->option.vbs >= 3) fprintf(stdout, " (W "col_GPSweek"%d"col_TXT") ", gpx->week);
fprintf(stdout, col_GPSTOW"%s"col_TXT" ", weekday[gpx->wday]);
}
fprintf(stdout, col_GPSdate"%04d-%02d-%02d"col_TXT" "col_GPSTOW"%02d:%02d:%06.3f"col_TXT" ",
gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek);
fprintf(stdout, " lat: "col_GPSlat"%.5f"col_TXT" ", gpx->lat);
@ -759,7 +883,7 @@ static int print_pos(gpx_t *gpx, int csOK) {
fprintf(stdout, " alt: "col_GPSalt"%.2f"col_TXT" ", gpx->alt);
if (!err2) {
//if (gpx->option.vbs == 2) fprintf(stdout, " "col_GPSvel"(%.1f , %.1f : %.1f)"col_TXT" ", gpx->vx, gpx->vy, gpx->vD2);
fprintf(stdout, " vH: "col_GPSvel"%.1f"col_TXT" D: "col_GPSvel"%.1f"col_TXT" vV: "col_GPSvel"%.1f"col_TXT" BV: "col_GPSvel"%.2f"col_TXT, gpx->vH, gpx->vD, gpx->vV, gpx->bLevel);
fprintf(stdout, " vH: "col_GPSvel"%.1f"col_TXT" D: "col_GPSvel"%.1f"col_TXT" vV: "col_GPSvel"%.1f"col_TXT" ", gpx->vH, gpx->vD, gpx->vV);
}
if (gpx->option.vbs >= 2) {
get_SN(gpx);
@ -770,23 +894,27 @@ static int print_pos(gpx_t *gpx, int csOK) {
if (csOK) fprintf(stdout, " "col_CSok"[OK]"col_TXT);
else fprintf(stdout, " "col_CSno"[NO]"col_TXT);
}
if (gpx->option.ptu) {
float t = get_Temp(gpx, csOK);
float rh = get_RH(gpx, csOK);
if (t > -270.0) fprintf(stdout, " T=%.1fC ", t);
if (gpx->option.vbs >= 3) { if (rh > -0.5) fprintf(stdout, "_RH=%.0f%% ", rh); }
if (gpx->option.ptu && csOK) {
if (gpx->T > -270.0) fprintf(stdout, " T=%.1fC", gpx->T);
if (gpx->option.vbs >= 2) { if (gpx->_RH > -0.5) fprintf(stdout, " _RH=%.0f%%", gpx->_RH); }
if (gpx->option.vbs >= 3) {
float t2 = get_Tntc2(gpx, csOK);
float fq555 = get_TLC555freq(gpx, get_count_RH(gpx));
if (t2 > -270.0) fprintf(stdout, " (T2:%.1fC) (%.3fkHz) ", t2, fq555/1e3);
fprintf(stdout, "(cRH=%.1f%%) ", get_RHc(gpx,csOK));
float t2 = get_Tntc2(gpx);
float fq555 = get_TLC555freq(gpx);
fprintf(stdout, " (Ti:%.1fC)", gpx->Ti);
if (t2 > -270.0) fprintf(stdout, " (T2:%.1fC) (%.3fkHz)", t2, fq555/1e3);
}
}
if (gpx->option.vbs >= 3 && csOK) {
fprintf(stdout, " (bat:%.2fV)", gpx->batV);
}
fprintf(stdout, ANSI_COLOR_RESET"");
}
else {
if (gpx->option.vbs >= 3) fprintf(stdout, " (W %d) ", gpx->week);
fprintf(stdout, "%s ", weekday[gpx->wday]);
if (gpx->type == t_M10)
{
if (gpx->option.vbs >= 3) fprintf(stdout, " (W %d) ", gpx->week);
fprintf(stdout, "%s ", weekday[gpx->wday]);
}
fprintf(stdout, "%04d-%02d-%02d %02d:%02d:%06.3f ",
gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek);
fprintf(stdout, " lat: %.5f ", gpx->lat);
@ -794,7 +922,7 @@ static int print_pos(gpx_t *gpx, int csOK) {
fprintf(stdout, " alt: %.2f ", gpx->alt);
if (!err2) {
//if (gpx->option.vbs == 2) fprintf(stdout, " (%.1f , %.1f : %.1f) ", gpx->vx, gpx->vy, gpx->vD2);
fprintf(stdout, " vH: %.1f D: %.1f vV: %.1f BV: %.2f", gpx->vH, gpx->vD, gpx->vV, gpx->bLevel);
fprintf(stdout, " vH: %.1f D: %.1f vV: %.1f ", gpx->vH, gpx->vD, gpx->vV);
}
if (gpx->option.vbs >= 2) {
get_SN(gpx);
@ -804,18 +932,19 @@ static int print_pos(gpx_t *gpx, int csOK) {
fprintf(stdout, " # ");
if (csOK) fprintf(stdout, " [OK]"); else fprintf(stdout, " [NO]");
}
if (gpx->option.ptu) {
float t = get_Temp(gpx, csOK);
float rh = get_RH(gpx, csOK);
if (t > -270.0) fprintf(stdout, " T=%.1fC ", t);
if (gpx->option.vbs >= 3) { if (rh > -0.5) fprintf(stdout, "_RH=%.0f%% ", rh); }
if (gpx->option.ptu && csOK) {
if (gpx->T > -270.0) fprintf(stdout, " T=%.1fC", gpx->T);
if (gpx->option.vbs >= 2) { if (gpx->_RH > -0.5) fprintf(stdout, " _RH=%.0f%%", gpx->_RH); }
if (gpx->option.vbs >= 3) {
float t2 = get_Tntc2(gpx, csOK);
float fq555 = get_TLC555freq(gpx,get_count_RH(gpx));
if (t2 > -270.0) fprintf(stdout, " (T2:%.1fC) (%.3fkHz) ", t2, fq555/1e3);
fprintf(stdout, "(cRH=%.1f%%) ", get_RHc(gpx,csOK));
float t2 = get_Tntc2(gpx);
float fq555 = get_TLC555freq(gpx);
fprintf(stdout, " (Ti:%.1fC)", gpx->Ti);
if (t2 > -270.0) fprintf(stdout, " (T2:%.1fC) (%.3fkHz)", t2, fq555/1e3);
}
}
if (gpx->option.vbs >= 3 && csOK) {
fprintf(stdout, " (bat:%.2fV)", gpx->batV);
}
}
fprintf(stdout, "\n");
@ -836,35 +965,45 @@ static int print_pos(gpx_t *gpx, int csOK) {
utc_week -= 1;
utc_s += 604800; // 604800sec = 1week
}
Gps2Date(utc_week, utc_s, &utc_jahr, &utc_monat, &utc_tag);
utc_s %= (24*3600); // 86400sec = 1day
utc_std = utc_s/3600;
utc_min = (utc_s%3600)/60;
utc_sek = utc_s%60 + (gpx->tow_ms % 1000)/1000.0;
if (gpx->type == t_M10) {
Gps2Date(utc_week, utc_s, &utc_jahr, &utc_monat, &utc_tag);
utc_s %= (24*3600); // 86400sec = 1day
utc_std = utc_s/3600;
utc_min = (utc_s%3600)/60;
utc_sek = utc_s%60 + (gpx->tow_ms % 1000)/1000.0;
}
else {
utc_jahr = gpx->jahr;
utc_monat = gpx->monat;
utc_tag = gpx->tag;
utc_std = gpx->std;
utc_min = gpx->min;
utc_sek = gpx->sek;
}
strncpy(sn_id+4, gpx->SN, 12);
sn_id[15] = '\0';
for (j = 0; sn_id[j]; j++) { if (sn_id[j] == ' ') sn_id[j] = '-'; }
fprintf(stdout, "{ ");
fprintf(stdout, "\"frame\": %lu ,", (unsigned long)(sec_gps0+0.5));
fprintf(stdout, "\"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f, \"sats\": %d, \"batt\": %.2f",
sn_id, utc_jahr, utc_monat, utc_tag, utc_std, utc_min, utc_sek, gpx->lat, gpx->lon, gpx->alt, gpx->vH, gpx->vD, gpx->vV, gpx->numSV, gpx->bLevel);
float rh = get_RH(gpx, csOK);
if (gpx->option.ptu && rh > -0.5) {
fprintf(stdout, ", \"humidity\": %.1f", rh );
}
fprintf(stdout, "{ \"type\": \"%s\"", "M10");
fprintf(stdout, ", \"frame\": %lu ,", (unsigned long)(sec_gps0+0.5));
fprintf(stdout, "\"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f, \"sats\": %d",
sn_id, utc_jahr, utc_monat, utc_tag, utc_std, utc_min, utc_sek, gpx->lat, gpx->lon, gpx->alt, gpx->vH, gpx->vD, gpx->vV, gpx->numSV);
// APRS id, 9 characters
aprs_id[0] = gpx->frame_bytes[pos_SN+2];
aprs_id[1] = gpx->frame_bytes[pos_SN] & 0xF;
aprs_id[2] = gpx->frame_bytes[pos_SN+4];
aprs_id[3] = gpx->frame_bytes[pos_SN+3];
fprintf(stdout, ", \"aprsid\": \"ME%02X%1X%02X%02X\"", aprs_id[0], aprs_id[1], aprs_id[2], aprs_id[3]);
// temperature
fprintf(stdout, ", \"batt\": %.2f", gpx->batV);
// temperature (and humidity)
if (gpx->option.ptu) {
float t = get_Temp(gpx, 0);
if (t > -273.0) fprintf(stdout, ", \"temp\": %.1f", t);
if (gpx->T > -273.0) fprintf(stdout, ", \"temp\": %.1f", gpx->T);
if (gpx->option.vbs >= 2) {
if (gpx->_RH > -0.5) fprintf(stdout, ", \"humidity\": %.1f", gpx->_RH);
}
}
fprintf(stdout, ", \"subtype\": \"0x%02X\"", gpx->type);
fprintf(stdout, " }\n");
fprintf(stdout, "\n");
}
@ -892,20 +1031,40 @@ static int print_frame(gpx_t *gpx, int pos) {
cs1 = (gpx->frame_bytes[pos_Check+gpx->auxlen] << 8) | gpx->frame_bytes[pos_Check+gpx->auxlen+1];
cs2 = checkM10(gpx->frame_bytes, pos_Check+gpx->auxlen);
switch (gpx->frame_bytes[1]) {
case 0x8F: gpx->type = t_M2K2; break;
case 0x9F: gpx->type = t_M10; break;
case 0xAF: gpx->type = t_M10plus; break;
case 0x20: gpx->type = t_M20; break;
default : gpx->type = t_M10;
}
if (gpx->option.raw) {
if (gpx->option.col && gpx->frame_bytes[1] != 0x49) {
if (gpx->option.col && gpx->frame_bytes[1] != 0x49 && (gpx->type == t_M10 || gpx->type == t_M10plus)) {
fprintf(stdout, col_FRTXT);
for (i = 0; i < FRAME_LEN+gpx->auxlen; i++) {
byte = gpx->frame_bytes[i];
if ((i >= pos_GPSTOW) && (i < pos_GPSTOW+4)) fprintf(stdout, col_GPSTOW);
if ((i >= pos_GPSlat) && (i < pos_GPSlat+4)) fprintf(stdout, col_GPSlat);
if ((i >= pos_GPSlon) && (i < pos_GPSlon+4)) fprintf(stdout, col_GPSlon);
if ((i >= pos_GPSalt) && (i < pos_GPSalt+4)) fprintf(stdout, col_GPSalt);
if ((i >= pos_GPSweek) && (i < pos_GPSweek+2)) fprintf(stdout, col_GPSweek);
if ((i >= pos_GPSvE) && (i < pos_GPSvE+6)) fprintf(stdout, col_GPSvel);
if ((i >= pos_SN) && (i < pos_SN+5)) fprintf(stdout, col_SN);
if ((i >= pos_Check+gpx->auxlen) && (i < pos_Check+gpx->auxlen+2)) fprintf(stdout, col_Check);
if (i == 1) fprintf(stdout, col_Mtype);
if (gpx->type == t_M10) {
if ((i >= pos_GPSTOW) && (i < pos_GPSTOW+4)) fprintf(stdout, col_GPSTOW);
if ((i >= pos_GPSlat) && (i < pos_GPSlat+4)) fprintf(stdout, col_GPSlat);
if ((i >= pos_GPSlon) && (i < pos_GPSlon+4)) fprintf(stdout, col_GPSlon);
if ((i >= pos_GPSalt) && (i < pos_GPSalt+4)) fprintf(stdout, col_GPSalt);
if ((i >= pos_GPSweek) && (i < pos_GPSweek+2)) fprintf(stdout, col_GPSweek);
if ((i >= pos_GPSvE) && (i < pos_GPSvE+6)) fprintf(stdout, col_GPSvel);
}
else {
if ((i >= pos_gtopGPSlat) && (i < pos_gtopGPSlat+4)) fprintf(stdout, col_GPSlat);
if ((i >= pos_gtopGPSlon) && (i < pos_gtopGPSlon+4)) fprintf(stdout, col_GPSlon);
if ((i >= pos_gtopGPSalt) && (i < pos_gtopGPSalt+3)) fprintf(stdout, col_GPSalt);
if ((i >= pos_gtopGPSvE) && (i < pos_gtopGPSvE+6)) fprintf(stdout, col_GPSvel);
if ((i >= pos_gtopGPStime) && (i < pos_gtopGPStime+3)) fprintf(stdout, col_GPSTOW);
if ((i >= pos_gtopGPSdate) && (i < pos_gtopGPSdate+3)) fprintf(stdout, col_GPSweek);
}
if ((i >= pos_SN) && (i < pos_SN+5)) fprintf(stdout, col_SN);
if (i == pos_CNT) fprintf(stdout, col_CNT);
if ((i >= pos_Check+gpx->auxlen) && (i < pos_Check+gpx->auxlen+2)) fprintf(stdout, col_Check);
fprintf(stdout, "%02x", byte);
fprintf(stdout, col_FRTXT);
}
@ -1215,7 +1374,6 @@ int main(int argc, char **argv) {
}
}
free_buffers(&dsp);
fclose(fp);

970
demod/mod/mXXmod.c 100644
Wyświetl plik

@ -0,0 +1,970 @@
/*
* mXX m18/m20 (test)
*
* (cf. mXX_20180919.c)
* sync header: correlation/matched filter
* files: mXXmod.c demod_mod.h demod_mod.c
* compile:
* gcc -c demod_mod.c
* gcc mXXmod.c demod_mod.o -lm -o mXXmod
*
* 2018-09-19 Ury: (len=0x43) ./mXX -c -vv --br 9600 mXX_20180919.wav
* 2019-11-06 Ury: (len=0x45) ./mXX -c -vv --br 9600 mXX_20191106.wav
* 2020-02-14 Ury: (len=0x45) ./mXX -c -vv --br 9600 mXX_20200214.wav
* 2020-05-11 Wien: (len=0x45) ./mXX -c -vv --br 9603 mXX_20200511.wav
*
* author: zilog80
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef CYGWIN
#include <fcntl.h> // cygwin: _setmode()
#include <io.h>
#endif
#include "demod_mod.h"
typedef struct {
i8_t vbs; // verbose output
i8_t raw; // raw frames
i8_t crc; // CRC check output
i8_t ecc; // Reed-Solomon ECC
i8_t sat; // GPS sat data
i8_t ptu; // PTU: temperature
i8_t inv;
i8_t aut;
i8_t col; // colors
i8_t jsn; // JSON output (auto_rx)
} option_t;
// ? 9600 baud M20 <-> 9616 baud M10 ?
#define BAUD_RATE 9600 // 9600..9604 // 9614..9616
/* -------------------------------------------------------------------------- */
/*
Header = Sync-Header + Sonde-Header:
1100110011001100 1010011001001100 1101010011010011 0100110101010101 0011010011001100
uudduudduudduudd ududduuddudduudd uudududduududduu dudduudududududu dduududduudduudd (oder:)
dduudduudduudduu duduudduuduudduu ddududuudduduudd uduuddududududud uudduduudduudduu (komplement)
0 0 0 0 0 0 0 0 1 1 - - - 0 0 0 0 1 1 0 0 1 0 0 1 0 0 1 1 1 1 1 0 0 1 0 0 0 0 0
*/
#define BITS 8
#define HEADLEN 32 // HEADLEN+HEADOFS=32 <= strlen(header)
#define HEADOFS 0
// Sync-Header (raw) // Sonde-Header (bits)
//char head[] = "11001100110011001010011001001100"; //"0110010010011111"; // M10: 64 9F , M2K2: 64 8F
//"0111011010011111"; // M10: 76 9F , w/ aux-data
//"0110010001001001"; // M10-dop: 64 49 09
//"0110010010101111"; // M10+: 64 AF w/ gtop-GPS
static char rawheader[] = "10011001100110010100110010011001";
#define FRAME_LEN (100+1) // 0x64+1
#define BITFRAME_LEN (FRAME_LEN*BITS)
#define AUX_LEN 20
#define BITAUX_LEN (AUX_LEN*BITS)
typedef struct {
int week; int tow_ms; int gpssec;
int jahr; int monat; int tag;
int wday;
int std; int min; float sek;
double lat; double lon; double alt;
double vH; double vD; double vV;
double vx; double vy; double vD2;
ui8_t numSV;
ui8_t utc_ofs;
char SN[12];
ui8_t frame_bytes[FRAME_LEN+AUX_LEN+4];
char frame_bits[BITFRAME_LEN+BITAUX_LEN+8];
int auxlen; // 0 .. 0x76-0x64
option_t option;
} gpx_t;
/* -------------------------------------------------------------------------- */
#define SECONDS_IN_WEEK (604800.0) // 7*86400
/*
* Convert GPS Week and Seconds to Modified Julian Day.
* - Adapted from sci.astro FAQ.
* - Ignores UTC leap seconds.
*/
static void Gps2Date(long GpsWeek, long GpsSeconds, int *Year, int *Month, int *Day) {
long GpsDays, Mjd;
long J, C, Y, M;
GpsDays = GpsWeek * 7 + (GpsSeconds / 86400);
Mjd = 44244 + GpsDays;
J = Mjd + 2468570;
C = 4 * J / 146097;
J = J - (146097 * C + 3) / 4;
Y = 4000 * (J + 1) / 1461001;
J = J - 1461 * Y / 4 + 31;
M = 80 * J / 2447;
*Day = J - 2447 * M / 80;
J = M / 11;
*Month = M + 2 - (12 * J);
*Year = 100 * (C - 49) + Y + J;
}
/* -------------------------------------------------------------------------- */
static int bits2bytes(char *bitstr, ui8_t *bytes) {
int i, bit, d, byteval;
int bitpos, bytepos;
bitpos = 0;
bytepos = 0;
while (bytepos < FRAME_LEN+AUX_LEN) {
byteval = 0;
d = 1;
for (i = 0; i < BITS; i++) {
//bit=*(bitstr+bitpos+i); /* little endian */
bit=*(bitstr+bitpos+7-i); /* big endian */
// bit == 'x' ?
if (bit == '1') byteval += d;
else /*if ((bit == '0') || (bit == 'x'))*/ byteval += 0;
d <<= 1;
}
bitpos += BITS;
bytes[bytepos++] = byteval & 0xFF;
}
//while (bytepos < FRAME_LEN+AUX_LEN) bytes[bytepos++] = 0;
return 0;
}
/* -------------------------------------------------------------------------- */
/*
M20
GPS data: Big Endian
PTU/ADC data: little endian
frame[0x0] = framelen // (0x43,) 0x45
frame[0x1] = 0x20 (type M20)
frame[0x02..0x18]: most important data at beginning (incl. counter + M10check)
frame[0x02..0x03]: ADC
frame[0x04..0x05]: ADC
frame[0x06..0x07]: ADC temperature
frame[0x08..0x0A]: GPS altitude
frame[0x0B..0x0E]: GPS hor.Vel. (velE,velN)
frame[0x0F..0x11]: GPS TOW
frame[0x15]: counter
frame[0x16..0x17]: block check
frame[0x18..0x19]: GPS ver.Vel. (velU)
frame[0x1A..0x1B]: GPS week
frame[0x1C..0x1F]: GPS latitude
frame[0x20..0x23]: GPS longitude
frame[0x44..0x45]: frame check
*/
#define stdFLEN 0x64 // pos[0]=0x45 // M20: 0x45 (0x43) M10: 0x64
#define pos_GPSTOW 0x0F // 3 byte
#define pos_GPSlat 0x1C // 4 byte
#define pos_GPSlon 0x20 // 4 byte
#define pos_GPSalt 0x08 // 3 byte
//#define pos_GPSsats 0xXX // 1 byte
//#define pos_GPSutc 0xXX // 1 byte
#define pos_GPSweek 0x1A // 2 byte
//Velocity East-North-Up (ENU)
#define pos_GPSvE 0x0B // 2 byte
#define pos_GPSvN 0x0D // 2 byte
#define pos_GPSvU 0x18 // 2 byte
#define pos_Cnt 0x15 // 1 byte
#define pos_BlkChk 0x16 // 2 byte
#define pos_Check (stdFLEN-1) // 2 byte
#define len_BlkChk 0x16 // frame[0x02..0x17] , incl. chk16
#define ANSI_COLOR_RED "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE "\x1b[34m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"
#define XTERM_COLOR_BROWN "\x1b[38;5;94m" // 38;5;{0..255}m
#define col_GPSweek "\x1b[38;5;20m" // 2 byte
#define col_GPSTOW "\x1b[38;5;27m" // 3 byte
#define col_GPSdate "\x1b[38;5;94m" //111
#define col_GPSlat "\x1b[38;5;34m" // 4 byte
#define col_GPSlon "\x1b[38;5;70m" // 4 byte
#define col_GPSalt "\x1b[38;5;82m" // 3 byte
#define col_GPSvel "\x1b[38;5;36m" // 6 byte
#define col_SN "\x1b[38;5;58m" // 3 byte
#define col_Check "\x1b[38;5;11m" // 2 byte
#define col_TXT "\x1b[38;5;244m"
#define col_FRTXT "\x1b[38;5;244m"
#define col_CSok "\x1b[38;5;2m"
#define col_CSno "\x1b[38;5;1m"
#define col_CNST "\x1b[38;5;58m" // 3 byte
/*
$ for code in {0..255}
> do echo -e "\e[38;5;${code}m"'\\e[38;5;'"$code"m"\e[0m"
> done
*/
static int get_GPSweek(gpx_t *gpx) {
int i;
unsigned byte;
ui8_t gpsweek_bytes[2];
int gpsweek;
//gpx->numSV = gpx->frame_bytes[pos_GPSsats];
//gpx->utc_ofs = gpx->frame_bytes[pos_GPSutc];
for (i = 0; i < 2; i++) {
byte = gpx->frame_bytes[pos_GPSweek + i];
gpsweek_bytes[i] = byte;
}
gpsweek = (gpsweek_bytes[0] << 8) + gpsweek_bytes[1];
if (gpsweek > 4000) return -1;
// Trimble Copernicus II WNRO (AirPrime XM1110 OK)
if (gpsweek < 1304 /*2005-01-02*/ ) gpsweek += 1024;
gpx->week = gpsweek;
return 0;
}
//char weekday[7][3] = { "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"};
static char weekday[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static int get_GPStime(gpx_t *gpx) {
int i;
unsigned byte;
ui8_t gpstime_bytes[4];
int gpstime, day;
int ms;
for (i = 0; i < 3; i++) {
byte = gpx->frame_bytes[pos_GPSTOW + i];
gpstime_bytes[i] = byte;
}
gpstime = 0;
for (i = 0; i < 3; i++) {
gpstime |= gpstime_bytes[i] << (8*(2-i));
}
gpx->tow_ms = gpstime*1000;
ms = 0;//gpstime % 1000;
//gpstime /= 1000;
gpx->gpssec = gpstime;
day = gpstime / (24 * 3600);
if ((day < 0) || (day > 6)) return -1;
gpstime %= (24*3600);
gpx->wday = day;
gpx->std = gpstime/3600;
gpx->min = (gpstime%3600)/60;
gpx->sek = gpstime%60 + ms/1000.0;
return 0;
}
//static double B60B60 = (1<<30)/90.0; // 2^32/360 = 2^30/90 = 0xB60B60.711x // M10
static double B60B60 = 1e6; // M20
static int get_GPSlat(gpx_t *gpx) {
int i;
unsigned byte;
ui8_t gpslat_bytes[4];
int gpslat;
double lat;
for (i = 0; i < 4; i++) {
byte = gpx->frame_bytes[pos_GPSlat + i];
gpslat_bytes[i] = byte;
}
gpslat = 0;
for (i = 0; i < 4; i++) {
gpslat |= gpslat_bytes[i] << (8*(3-i));
}
lat = gpslat / B60B60;
gpx->lat = lat;
return 0;
}
static int get_GPSlon(gpx_t *gpx) {
int i;
unsigned byte;
ui8_t gpslon_bytes[4];
int gpslon;
double lon;
for (i = 0; i < 4; i++) {
byte = gpx->frame_bytes[pos_GPSlon + i];
gpslon_bytes[i] = byte;
}
gpslon = 0;
for (i = 0; i < 4; i++) {
gpslon |= gpslon_bytes[i] << (8*(3-i));
}
lon = gpslon / B60B60;
gpx->lon = lon;
return 0;
}
static int get_GPSalt(gpx_t *gpx) { // 24 bit
int i;
unsigned byte;
ui8_t gpsalt_bytes[4];
int gpsalt;
double alt;
for (i = 0; i < 3; i++) {
byte = gpx->frame_bytes[pos_GPSalt + i];
gpsalt_bytes[i] = byte;
}
gpsalt = 0;
for (i = 0; i < 3; i++) {
gpsalt |= gpsalt_bytes[i] << (8*(2-i));
}
alt = gpsalt / 100.0;
gpx->alt = alt;
return 0;
}
static int get_GPSvel(gpx_t *gpx) {
int i;
unsigned byte;
ui8_t gpsVel_bytes[2];
short vel16;
double vx, vy, dir, alpha;
const double ms2kn100 = 1e2; //2e2; // m/s -> knots: 1 m/s = 3.6/1.852 kn = 1.94 kn
for (i = 0; i < 2; i++) {
byte = gpx->frame_bytes[pos_GPSvE + i];
gpsVel_bytes[i] = byte;
}
vel16 = gpsVel_bytes[0] << 8 | gpsVel_bytes[1];
vx = vel16 / ms2kn100; // ost
for (i = 0; i < 2; i++) {
byte = gpx->frame_bytes[pos_GPSvN + i];
gpsVel_bytes[i] = byte;
}
vel16 = gpsVel_bytes[0] << 8 | gpsVel_bytes[1];
vy= vel16 / ms2kn100; // nord
gpx->vx = vx;
gpx->vy = vy;
gpx->vH = sqrt(vx*vx+vy*vy);
///*
alpha = atan2(vy, vx)*180/M_PI; // ComplexPlane (von x-Achse nach links) - GeoMeteo (von y-Achse nach rechts)
dir = 90-alpha; // z=x+iy= -> i*conj(z)=y+ix=re(i(pi/2-t)), Achsen und Drehsinn vertauscht
if (dir < 0) dir += 360; // atan2(y,x)=atan(y/x)=pi/2-atan(x/y) , atan(1/t) = pi/2 - atan(t)
gpx->vD2 = dir;
//*/
dir = atan2(vx, vy) * 180 / M_PI;
if (dir < 0) dir += 360;
gpx->vD = dir;
for (i = 0; i < 2; i++) {
byte = gpx->frame_bytes[pos_GPSvU + i];
gpsVel_bytes[i] = byte;
}
vel16 = gpsVel_bytes[0] << 8 | gpsVel_bytes[1];
gpx->vV = vel16 / ms2kn100;
return 0;
}
/* -------------------------------------------------------------------------- */
/*
g : F^n -> F^16 // checksum, linear
g(m||b) = f(g(m),b)
// update checksum
f : F^16 x F^8 -> F^16 linear
010100001000000101000000
001010000100000010100000
000101000010000001010000
000010100001000000101000
000001010000100000010100
100000100000010000001010
000000011010100000000100
100000000101010000000010
000000001000000000000000
000000000100000000000000
000000000010000000000000
000000000001000000000000
000000000000100000000000
000000000000010000000000
000000000000001000000000
000000000000000100000000
*/
static int update_checkM10(int c, ui8_t b) {
int c0, c1, t, t6, t7, s;
c1 = c & 0xFF;
// B
b = (b >> 1) | ((b & 1) << 7);
b ^= (b >> 2) & 0xFF;
// A1
t6 = ( c & 1) ^ ((c>>2) & 1) ^ ((c>>4) & 1);
t7 = ((c>>1) & 1) ^ ((c>>3) & 1) ^ ((c>>5) & 1);
t = (c & 0x3F) | (t6 << 6) | (t7 << 7);
// A2
s = (c >> 7) & 0xFF;
s ^= (s >> 2) & 0xFF;
c0 = b ^ t ^ s;
return ((c1<<8) | c0) & 0xFFFF;
}
static int checkM10(ui8_t *msg, int len) {
int i, cs; // msg[0] = len+1
cs = 0;
for (i = 0; i < len; i++) {
cs = update_checkM10(cs, msg[i]);
}
return cs & 0xFFFF;
}
// checkM10(frame, frame[0]-1) = blk_checkM10(frame[0], frame+1)
static int blk_checkM10(int len, ui8_t *msg) {
int i, cs;
ui8_t pre = len & 0xFF; // len(block+chk16)
cs = 0;
cs = update_checkM10(cs, pre);
for (i = 0; i < len-2; i++) {
cs = update_checkM10(cs, msg[i]);
}
return cs & 0xFFFF;
}
/* -------------------------------------------------------------------------- */
static float get_Tntc0(gpx_t *gpx) {
// SMD ntc
float Rs = 22.1e3; // P5.6=Vcc
float R25 = 2.2e3;// 0.119e3; //2.2e3;
float b = 3650.0; // B/Kelvin
float T25 = 25.0 + 273.15; // T0=25C, R0=R25=5k
// -> Steinhart–Hart coefficients (polyfit):
float p0 = 4.42606809e-03,
p1 = -6.58184309e-04,
p2 = 8.95735557e-05,
p3 = -2.84347503e-06;
float T = 0.0; // T/Kelvin
ui16_t ADC_ntc0; // M10: ADC12 P6.4(A4)
float x, R;
ADC_ntc0 = (gpx->frame_bytes[0x07] << 8) | gpx->frame_bytes[0x06]; // M10: 0x40,0x3F
x = (4095.0 - ADC_ntc0)/ADC_ntc0; // (Vcc-Vout)/Vout
R = Rs / x;
if (R > 0) T = 1/(1/T25 + 1/b * log(R/R25));
//if (R > 0) T = 1/( p0 + p1*log(R) + p2*log(R)*log(R) + p3*log(R)*log(R)*log(R) );
return T - 273.15;
}
/* -------------------------------------------------------------------------- */
static int print_pos(gpx_t *gpx, int bcOK, int csOK) {
int err, err2;
err = 0;
err |= get_GPSweek(gpx);
err |= get_GPStime(gpx);
err |= get_GPSlat(gpx);
err |= get_GPSlon(gpx);
err |= get_GPSalt(gpx);
err2 = get_GPSvel(gpx);
if (!err) {
Gps2Date(gpx->week, gpx->gpssec, &gpx->jahr, &gpx->monat, &gpx->tag);
if (gpx->option.col) {
fprintf(stdout, col_TXT);
if (gpx->option.vbs >= 3) {
fprintf(stdout, "[%3d]", gpx->frame_bytes[pos_Cnt]);
fprintf(stdout, " (W "col_GPSweek"%d"col_TXT") ", gpx->week);
}
fprintf(stdout, col_GPSTOW"%s"col_TXT" ", weekday[gpx->wday]);
fprintf(stdout, col_GPSdate"%04d-%02d-%02d"col_TXT" "col_GPSTOW"%02d:%02d:%06.3f"col_TXT" ",
gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek);
fprintf(stdout, " lat: "col_GPSlat"%.5f"col_TXT" ", gpx->lat);
fprintf(stdout, " lon: "col_GPSlon"%.5f"col_TXT" ", gpx->lon);
fprintf(stdout, " alt: "col_GPSalt"%.2f"col_TXT" ", gpx->alt);
if (!err2) {
fprintf(stdout, " vH: "col_GPSvel"%.1f"col_TXT" D: "col_GPSvel"%.1f"col_TXT" vV: "col_GPSvel"%.1f"col_TXT" ", gpx->vH, gpx->vD, gpx->vV);
}
if (gpx->option.vbs >= 3 && bcOK) {
ui32_t byte = (gpx->frame_bytes[0x14]<<8) | gpx->frame_bytes[0x13];
fprintf(stdout, " ( %04u)", (byte>>2)&0x1FFF);
}
if (gpx->option.vbs >= 2) {
fprintf(stdout, " # ");
if (bcOK) fprintf(stdout, " "col_CSok"(ok)"col_TXT);
else fprintf(stdout, " "col_CSno"(no)"col_TXT);
if (csOK) fprintf(stdout, " "col_CSok"[OK]"col_TXT);
else fprintf(stdout, " "col_CSno"[NO]"col_TXT);
}
if (gpx->option.ptu && csOK) {
if (gpx->option.vbs >= 3) {
float t0 = get_Tntc0(gpx);
if (t0 > -270.0) fprintf(stdout, " (T0:%.1fC) ", t0);
}
}
fprintf(stdout, ANSI_COLOR_RESET"");
}
else {
if (gpx->option.vbs >= 3) {
fprintf(stdout, "[%3d]", gpx->frame_bytes[pos_Cnt]);
fprintf(stdout, " (W %d) ", gpx->week);
}
fprintf(stdout, "%s ", weekday[gpx->wday]);
fprintf(stdout, "%04d-%02d-%02d %02d:%02d:%06.3f ",
gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek);
fprintf(stdout, " lat: %.5f ", gpx->lat);
fprintf(stdout, " lon: %.5f ", gpx->lon);
fprintf(stdout, " alt: %.2f ", gpx->alt);
if (!err2) {
fprintf(stdout, " vH: %.1f D: %.1f vV: %.1f ", gpx->vH, gpx->vD, gpx->vV);
}
if (gpx->option.vbs >= 2) {
fprintf(stdout, " # ");
if (bcOK) fprintf(stdout, " (ok)"); else fprintf(stdout, " (no)");
if (csOK) fprintf(stdout, " [OK]"); else fprintf(stdout, " [NO]");
}
if (gpx->option.ptu && csOK) {
if (gpx->option.vbs >= 3) {
float t0 = get_Tntc0(gpx);
if (t0 > -270.0) fprintf(stdout, " (T0:%.1fC) ", t0);
}
}
}
fprintf(stdout, "\n");
}
return err;
}
static int print_frame(gpx_t *gpx, int pos) {
int i;
ui8_t byte;
int cs1, cs2;
int bc1, bc2;
int flen = stdFLEN; // stdFLEN=0x64, auxFLEN=0x76; M20:0x45 ?
bits2bytes(gpx->frame_bits, gpx->frame_bytes);
flen = gpx->frame_bytes[0];
if (flen == stdFLEN) gpx->auxlen = 0;
else {
gpx->auxlen = flen - stdFLEN;
//if (gpx->auxlen < 0 || gpx->auxlen > AUX_LEN) gpx->auxlen = 0;
}
cs1 = (gpx->frame_bytes[pos_Check+gpx->auxlen] << 8) | gpx->frame_bytes[pos_Check+gpx->auxlen+1];
cs2 = checkM10(gpx->frame_bytes, pos_Check+gpx->auxlen);
bc1 = (gpx->frame_bytes[pos_BlkChk] << 8) | gpx->frame_bytes[pos_BlkChk+1];
bc2 = blk_checkM10(len_BlkChk, gpx->frame_bytes+2); // len(essentialBlock+chk16) = 0x16
if (gpx->option.raw) {
if (gpx->option.col && gpx->frame_bytes[1] != 0x49) {
fprintf(stdout, col_FRTXT);
for (i = 0; i < FRAME_LEN+gpx->auxlen; i++) {
byte = gpx->frame_bytes[i];
if ((i >= pos_GPSTOW) && (i < pos_GPSTOW+3)) fprintf(stdout, col_GPSTOW);
if ((i >= pos_GPSlat) && (i < pos_GPSlat+4)) fprintf(stdout, col_GPSlat);
if ((i >= pos_GPSlon) && (i < pos_GPSlon+4)) fprintf(stdout, col_GPSlon);
if ((i >= pos_GPSalt) && (i < pos_GPSalt+3)) fprintf(stdout, col_GPSalt);
if ((i >= pos_GPSweek) && (i < pos_GPSweek+2)) fprintf(stdout, col_GPSweek);
if ((i >= pos_GPSvE) && (i < pos_GPSvE+2)) fprintf(stdout, col_GPSvel);
if ((i >= pos_GPSvN) && (i < pos_GPSvN+2)) fprintf(stdout, col_GPSvel);
if ((i >= pos_GPSvU) && (i < pos_GPSvU+2)) fprintf(stdout, col_GPSvel);
if ((i >= pos_BlkChk) && (i < pos_BlkChk+2)) fprintf(stdout, col_Check);
if ((i >= pos_Check+gpx->auxlen) && (i < pos_Check+gpx->auxlen+2)) fprintf(stdout, col_Check);
fprintf(stdout, "%02x", byte);
fprintf(stdout, col_FRTXT);
}
if (gpx->option.vbs) {
fprintf(stdout, " # "col_Check"%04x"col_FRTXT, cs2);
if (bc1 == bc2) fprintf(stdout, " "col_CSok"(ok)"col_TXT);
else fprintf(stdout, " "col_CSno"(no)"col_TXT);
if (cs1 == cs2) fprintf(stdout, " "col_CSok"[OK]"col_TXT);
else fprintf(stdout, " "col_CSno"[NO]"col_TXT);
}
fprintf(stdout, ANSI_COLOR_RESET"\n");
}
else {
for (i = 0; i < FRAME_LEN+gpx->auxlen; i++) {
byte = gpx->frame_bytes[i];
fprintf(stdout, "%02x", byte);
}
if (gpx->option.vbs) {
fprintf(stdout, " # %04x", cs2);
if (bc1 == bc2) fprintf(stdout, " (ok)"); else fprintf(stdout, " (no)");
if (cs1 == cs2) fprintf(stdout, " [OK]"); else fprintf(stdout, " [NO]");
}
fprintf(stdout, "\n");
}
}
else if (gpx->frame_bytes[1] == 0x49) {
if (gpx->option.vbs == 3) {
for (i = 0; i < FRAME_LEN+gpx->auxlen; i++) {
byte = gpx->frame_bytes[i];
fprintf(stdout, "%02x", byte);
}
fprintf(stdout, "\n");
}
}
else print_pos(gpx, bc1 == bc2, cs1 == cs2);
return (gpx->frame_bytes[0]<<8)|gpx->frame_bytes[1];
}
int main(int argc, char **argv) {
int option_verbose = 0; // ausfuehrliche Anzeige
int option_raw = 0; // rohe Frames
int option_inv = 0; // invertiert Signal
//int option_res = 0; // genauere Bitmessung
int option_color = 0;
int option_ptu = 0;
int option_min = 0;
int option_iq = 0;
int option_lp = 0;
int option_dc = 0;
int option_pcmraw = 0;
int wavloaded = 0;
int sel_wavch = 0; // audio channel: left
int spike = 0;
float baudrate = -1;
FILE *fp = NULL;
char *fpname = NULL;
int k;
int bit, bit0;
int bitpos = 0;
int bitQ;
int pos;
//int headerlen = 0;
int header_found = 0;
float thres = 0.76;
float _mv = 0.0;
int symlen = 2;
int bitofs = 0; // 0 .. +2
int shift = 0;
pcm_t pcm = {0};
dsp_t dsp = {0}; //memset(&dsp, 0, sizeof(dsp));
gpx_t gpx = {0};
#ifdef CYGWIN
_setmode(fileno(stdin), _O_BINARY); // _setmode(_fileno(stdin), _O_BINARY);
#endif
setbuf(stdout, NULL);
fpname = argv[0];
++argv;
while ((*argv) && (!wavloaded)) {
if ( (strcmp(*argv, "-h") == 0) || (strcmp(*argv, "--help") == 0) ) {
fprintf(stderr, "%s [options] audio.wav\n", fpname);
fprintf(stderr, " options:\n");
//fprintf(stderr, " -v, --verbose\n");
fprintf(stderr, " -r, --raw\n");
fprintf(stderr, " -c, --color\n");
return 0;
}
else if ( (strcmp(*argv, "-v") == 0) || (strcmp(*argv, "--verbose") == 0) ) {
option_verbose = 1;
}
else if ( (strcmp(*argv, "-vv" ) == 0) ) option_verbose = 2;
else if ( (strcmp(*argv, "-vvv") == 0) ) option_verbose = 3;
else if ( (strcmp(*argv, "-r") == 0) || (strcmp(*argv, "--raw") == 0) ) {
option_raw = 1;
}
else if ( (strcmp(*argv, "-i") == 0) || (strcmp(*argv, "--invert") == 0) ) {
option_inv = 1; // nicht noetig
}
else if ( (strcmp(*argv, "-c") == 0) || (strcmp(*argv, "--color") == 0) ) {
option_color = 1;
}
else if ( (strcmp(*argv, "--br") == 0) ) {
++argv;
if (*argv) {
baudrate = atof(*argv);
if (baudrate < 9000 || baudrate > 10000) baudrate = BAUD_RATE; // default: 9615
}
else return -1;
}
//else if (strcmp(*argv, "--res") == 0) { option_res = 1; }
else if ( (strcmp(*argv, "--ptu") == 0) ) {
option_ptu = 1;
}
else if ( (strcmp(*argv, "--spike") == 0) ) {
spike = 1;
}
else if ( (strcmp(*argv, "--ch2") == 0) ) { sel_wavch = 1; } // right channel (default: 0=left)
else if ( (strcmp(*argv, "--ths") == 0) ) {
++argv;
if (*argv) {
thres = atof(*argv);
}
else return -1;
}
else if ( (strcmp(*argv, "-d") == 0) ) {
++argv;
if (*argv) {
shift = atoi(*argv);
if (shift > 4) shift = 4;
if (shift < -4) shift = -4;
}
else return -1;
}
else if (strcmp(*argv, "--iq0") == 0) { option_iq = 1; } // differential/FM-demod
else if (strcmp(*argv, "--iq2") == 0) { option_iq = 2; }
else if (strcmp(*argv, "--iq3") == 0) { option_iq = 3; } // iq2==iq3
else if (strcmp(*argv, "--IQ") == 0) { // fq baseband -> IF (rotate from and decimate)
double fq = 0.0; // --IQ <fq> , -0.5 < fq < 0.5
++argv;
if (*argv) fq = atof(*argv);
else return -1;
if (fq < -0.5) fq = -0.5;
if (fq > 0.5) fq = 0.5;
dsp.xlt_fq = -fq; // S(t) -> S(t)*exp(-f*2pi*I*t)
option_iq = 5;
}
else if (strcmp(*argv, "--lp") == 0) { option_lp = 1; } // IQ lowpass
else if (strcmp(*argv, "--dc") == 0) { option_dc = 1; }
else if (strcmp(*argv, "--min") == 0) {
option_min = 1;
}
else if (strcmp(*argv, "--json") == 0) { gpx.option.jsn = 1; }
else if (strcmp(*argv, "-") == 0) {
int sample_rate = 0, bits_sample = 0, channels = 0;
++argv;
if (*argv) sample_rate = atoi(*argv); else return -1;
++argv;
if (*argv) bits_sample = atoi(*argv); else return -1;
channels = 2;
if (sample_rate < 1 || (bits_sample != 8 && bits_sample != 16 && bits_sample != 32)) {
fprintf(stderr, "- <sr> <bs>\n");
return -1;
}
pcm.sr = sample_rate;
pcm.bps = bits_sample;
pcm.nch = channels;
option_pcmraw = 1;
}
else {
fp = fopen(*argv, "rb");
if (fp == NULL) {
fprintf(stderr, "error: open %s\n", *argv);
return -1;
}
wavloaded = 1;
}
++argv;
}
if (!wavloaded) fp = stdin;
gpx.option.inv = option_inv; // irrelevant
gpx.option.vbs = option_verbose;
gpx.option.raw = option_raw;
gpx.option.ptu = option_ptu;
gpx.option.col = option_color;
// init gpx
if (option_iq == 0 && option_pcmraw) {
fclose(fp);
fprintf(stderr, "error: raw data not IQ\n");
return -1;
}
if (option_iq) sel_wavch = 0;
pcm.sel_ch = sel_wavch;
if (option_pcmraw == 0) {
k = read_wav_header(&pcm, fp);
if ( k < 0 ) {
fclose(fp);
fprintf(stderr, "error: wav header\n");
return -1;
}
}
// m10: BT>1?, h=1.2 ?
symlen = 2;
// init dsp
//
dsp.fp = fp;
dsp.sr = pcm.sr;
dsp.bps = pcm.bps;
dsp.nch = pcm.nch;
dsp.ch = pcm.sel_ch;
dsp.br = (float)BAUD_RATE;
dsp.sps = (float)dsp.sr/dsp.br;
dsp.symlen = symlen;
dsp.symhd = 1; // M10!header
dsp._spb = dsp.sps*symlen;
dsp.hdr = rawheader;
dsp.hdrlen = strlen(rawheader);
dsp.BT = 1.8; // bw/time (ISI) // 1.0..2.0 // M20 ?
dsp.h = 0.9; // 1.2 modulation index // M20 ?
dsp.opt_iq = option_iq;
dsp.opt_lp = option_lp;
dsp.lpIQ_bw = 24e3; // IF lowpass bandwidth
dsp.lpFM_bw = 10e3; // FM audio lowpass
dsp.opt_dc = option_dc;
dsp.opt_IFmin = option_min;
if ( dsp.sps < 8 ) {
fprintf(stderr, "note: sample rate low (%.1f sps)\n", dsp.sps);
}
if (baudrate > 0) {
dsp.br = (float)baudrate;
dsp.sps = (float)dsp.sr/dsp.br;
fprintf(stderr, "sps corr: %.4f\n", dsp.sps);
}
//headerlen = dsp.hdrlen;
k = init_buffers(&dsp);
if ( k < 0 ) {
fprintf(stderr, "error: init buffers\n");
return -1;
};
bitofs += shift;
while ( 1 )
{
// FM-audio:
header_found = find_header(&dsp, thres, 2, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0
_mv = dsp.mv;
if (header_found == EOF) break;
// mv == correlation score
if (_mv*(0.5-gpx.option.inv) < 0) {
gpx.option.inv ^= 0x1; // M10: irrelevant
}
if (header_found) {
bitpos = 0;
pos = 0;
pos /= 2;
bit0 = '0'; // oder: _mv[j] > 0
while ( pos < BITFRAME_LEN+BITAUX_LEN ) {
if (option_iq >= 2) {
float bl = -1;
if (option_iq > 2) bl = 4.0;
bitQ = read_slbit(&dsp, &bit, 0/*gpx.option.inv*/, bitofs, bitpos, bl, 0);
}
else {
bitQ = read_slbit(&dsp, &bit, 0/*gpx.option.inv*/, bitofs, bitpos, -1, spike); // symlen=2
}
if ( bitQ == EOF ) { break; }
gpx.frame_bits[pos] = 0x31 ^ (bit0 ^ bit);
pos++;
bit0 = bit;
bitpos += 1;
}
gpx.frame_bits[pos] = '\0';
print_frame(&gpx, pos);
if (pos < BITFRAME_LEN) break;
header_found = 0;
// bis Ende der Sekunde vorspulen; allerdings Doppel-Frame alle 10 sek
if (gpx.option.vbs < 3) { // && (regulare frame) // print_frame-return?
while ( bitpos < 5*BITFRAME_LEN ) {
bitQ = read_slbit(&dsp, &bit, 0/*gpx.option.inv*/, bitofs, bitpos, -1, spike); // symlen=2
if ( bitQ == EOF) break;
bitpos++;
}
}
pos = 0;
}
}
free_buffers(&dsp);
fclose(fp);
return 0;
}

Wyświetl plik

@ -720,7 +720,8 @@ int main(int argc, char **argv) {
if (gpx.sn > 0 && gpx.sn < 1e9) {
sprintf(id_str, "%.0f", gpx.sn);
}
printf("{ \"frame\": %d, \"id\": \"IMS100-%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f }\n",
printf("{ \"type\": \"%s\"", "IMS100");
printf(", \"frame\": %d, \"id\": \"IMS100-%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f }\n",
gpx.frnr, id_str, gpx.jahr, gpx.monat, gpx.tag, gpx.std, gpx.min, gpx.sek, gpx.lat, gpx.lon, gpx.alt, gpx.vH, gpx.vD );
printf("\n");
}

Wyświetl plik

@ -2,7 +2,7 @@
/*
* rs41
* sync header: correlation/matched filter
* files: rs41mod.c bch_ecc_mod.c demod_mod.c demod_mod.h
* files: rs41mod.c bch_ecc_mod.c bch_ecc_mod.h demod_mod.c demod_mod.h
* compile, either (a) or (b):
* (a)
* gcc -c demod_mod.c
@ -1327,7 +1327,8 @@ static int print_position(gpx_t *gpx, int ec) {
// Print out telemetry data as JSON
if ((!err && !err1 && !err3) || (!err && encrypted)) { // frame-nb/id && gps-time && gps-position (crc-)ok; 3 CRCs, RS not needed
// eigentlich GPS, d.h. UTC = GPS - 18sec (ab 1.1.2017)
fprintf(stdout, "{ \"frame\": %d, \"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f, \"sats\": %d, \"bt\": %d, \"batt\": %.2f",
fprintf(stdout, "{ \"type\": \"%s\"", "RS41");
fprintf(stdout, ", \"frame\": %d, \"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f, \"sats\": %d, \"bt\": %d, \"batt\": %.2f",
gpx->frnr, gpx->id, gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek, gpx->lat, gpx->lon, gpx->alt, gpx->vH, gpx->vD, gpx->vV, gpx->numSV, gpx->conf_cd, gpx->batt );
if (gpx->option.ptu && !err0 && gpx->T > -273.0) {
fprintf(stdout, ", \"temp\": %.1f", gpx->T );

Wyświetl plik

@ -2,7 +2,7 @@
/*
* rs92
* sync header: correlation/matched filter
* files: rs92mod.c nav_gps_vel.c bch_ecc_mod.c demod_mod.c demod_mod.h
* files: rs92mod.c nav_gps_vel.c bch_ecc_mod.c bch_ecc_mod.h demod_mod.c demod_mod.h
* compile:
* (a)
* gcc -c demod_mod.c
@ -1177,7 +1177,9 @@ static int print_position(gpx_t *gpx, int ec) { // GPS-Hoehe ueber Ellipsoid
// Print out telemetry data as JSON //even if we don't have a valid GPS lock
if ((gpx->crc & (crc_FRAME | crc_GPS))==0 && (gpx->gps.almanac || gpx->gps.ephem)) //(!err1 && !err3)
{ // eigentlich GPS, d.h. UTC = GPS - UTC_OFS (UTC_OFS=18sec ab 1.1.2017)
fprintf(stdout, "\n{ \"frame\": %d, \"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f",
fprintf(stdout, "\n");
fprintf(stdout, "{ \"type\": \"%s\"", "RS92");
fprintf(stdout, ", \"frame\": %d, \"id\": \"%s\", \"datetime\": \"%04d-%02d-%02dT%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f",
gpx->frnr, gpx->id, gpx->jahr, gpx->monat, gpx->tag, gpx->std, gpx->min, gpx->sek, gpx->lat, gpx->lon, gpx->alt, gpx->vH, gpx->vD, gpx->vU);
if ((gpx->crc & crc_AUX)==0 && (gpx->aux[0] != 0 || gpx->aux[1] != 0 || gpx->aux[2] != 0 || gpx->aux[3] != 0)) {
fprintf(stdout, ", \"aux\": \"%04x%04x%04x%04x\"", gpx->aux[0], gpx->aux[1], gpx->aux[2], gpx->aux[3]);

Wyświetl plik

@ -427,7 +427,8 @@ int print_frame(int len) {
if (option_json) {
if (gpx.gps_valid && gpx.ptu_valid) // frameNb part of PTU-pck
{
fprintf(stdout, "{ \"frame\": %d, \"id\": \"iMet\", \"datetime\": \"%02d:%02d:%02dZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %d, \"sats\": %d, \"temp\": %.2f, \"humidity\": %.2f, \"pressure\": %.2f, \"batt\": %.1f }\n",
fprintf(stdout, "{ \"type\": \"%s\"", "IMET");
fprintf(stdout, ", \"frame\": %d, \"id\": \"iMet\", \"datetime\": \"%02d:%02d:%02dZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %d, \"sats\": %d, \"temp\": %.2f, \"humidity\": %.2f, \"pressure\": %.2f, \"batt\": %.1f }\n",
gpx.frame, gpx.hour, gpx.min, gpx.sec, gpx.lat, gpx.lon, gpx.alt, gpx.sats, gpx.temp, gpx.humidity, gpx.pressure, gpx.batt);
}
}

Wyświetl plik

@ -634,14 +634,17 @@ void print_frame(int len) {
if (option_jsn) {
// Print JSON output required by auto_rx.
if (gpx.prev_frnr != gpx.frnr){
if (crc_err==0 && (gpx.id & 0xFFFF0000)) { // CRC-OK and FullID
if (crc_err==0 && (gpx.id & 0xFFFF0000)) { // CRC-OK and FullID
if (gpx.prev_frnr != gpx.frnr) { //|| gpx.id != _id0
// UTC oder GPS?
printf("{ \"frame\": %d, \"id\": \"LMS6-%d\", \"datetime\": \"%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f }\n",
printf("{ \"type\": \"%s\"", "LMS");
printf(", \"frame\": %d, \"id\": \"LMS6-%d\", \"datetime\": \"%02d:%02d:%06.3fZ\", \"lat\": %.5f, \"lon\": %.5f, \"alt\": %.5f, \"vel_h\": %.5f, \"heading\": %.5f, \"vel_v\": %.5f",
gpx.frnr, gpx.id, gpx.std, gpx.min, gpx.sek, gpx.lat, gpx.lon, gpx.alt, gpx.vH, gpx.vD, gpx.vV );
printf(", \"subtype\": \"%s\"", "MK2A");
printf(" }\n");
printf("\n");
gpx.prev_frnr = gpx.frnr;
}
gpx.prev_frnr = gpx.frnr;
}
}

Wyświetl plik

@ -1,10 +1,22 @@
/*
* compile:
* gcc dft_detect.c -lm -o dft_detect
* speedup:
* gcc -Ofast dft_detect.c -lm -o dft_detect
*
* author: zilog80
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <complex.h>
#ifndef M_PI
#define M_PI (3.1415926535897932384626433832795)
#endif
typedef unsigned char ui8_t;
typedef unsigned short ui16_t;
@ -46,11 +58,12 @@ static char mk2a_header[] = "0010100111""0010100111""0001001001""0010010101";
//int m10_sps = 9600;
static char m10_header[] = "10011001100110010100110010011001";
// frame byte[0..1]: byte[0]=framelen-1, byte[1]=type(8F=M2K2,9F=M10,AF=M10+)
// M2K2 : 64 8F : 0110010010001111
// M10 : 64 9F : 0110010010011111 (framelen 0x64+1)
// M10-aux: 76 9F : 0111011010011111 (framelen 0x76+1)
// M10+ : 64 AF : 0110010010101111 (w/ gtop-GPS)
// frame byte[0..1]: byte[0]=framelen-1, byte[1]=type(8F=M2K2,9F=M10,AF=M10+,20=M20)
// M2K2 : 64 8F : 01100100 10001111
// M10 : 64 9F : 01100100 10011111 (framelen 0x64+1) (baud=9616)
// M10-aux: 76 9F : 01110110 10011111 (framelen 0x76+1)
// M10+ : 64 AF : 01100100 10101111 (w/ gtop-GPS)
// M20 : 45 20 : 01000101 00100000 (framelen 0x45+1) (baud=9600)
//int meisei_sps = 2400; // 0xFB6230 =
static char meisei_header[] = "110011001101001101001101010100101010110010101010"; // 11111011 01100010 00110000
@ -102,25 +115,35 @@ static float lpFM_bw[2] = { 4e3, 10e3 }; // FM-audio lowpass bandwidth
static float lpIQ_bw[3] = { 12e3, 22e3, 200e3 }; // IF iq lowpass bandwidth
static float set_lpIQ = 0.0;
#define tn_DFM 2
#define tn_RS41 3
#define tn_RS92 4
#define tn_M10 5
#define tn_M20 6
#define tn_LMS6 8
#define tn_MEISEI 9
#define tn_C34C50 10
#define tn_MK2LMS 21
#define tn_IMET 15 // IMET4=+1, IMET1RS=+3, IMET1AB=+4
#define Nrs 12
#define idxIMETs 8
#define idxAB 9
#define idxRS 10
#define idxI4 11
// Thresholds modified by VK5QI 2019-10-04
static rsheader_t rs_hdr[Nrs] = {
{ 2500, 0, 0, dfm_header, 1.0, 0.0, 0.62, 2, NULL, "DFM9", 2 , 0, 0, 0.0}, // DFM6: -2 ?
{ 4800, 0, 0, rs41_header, 0.5, 0.0, 0.53, 2, NULL, "RS41", 3 , 0, 0, 0.0},
{ 4800, 0, 0, rs92_header, 0.5, 0.0, 0.54, 3, NULL, "RS92", 4 , 0, 0, 0.0}, // RS92NGP: 1680/400=4.2
{ 4800, 0, 0, lms6_header, 1.0, 0.0, 0.70, 2, NULL, "LMS6", 8 , 0, 0, 0.0}, // lmsX: 7?
{ 9616, 0, 0, mk2a_header, 1.0, 0.0, 0.70, 2, NULL, "MK2LMS", 21 , 1, 2, 0.0}, // Mk2a/LMS6-1680 , --IQ: decimate > 170kHz ...
{ 9616, 0, 0, m10_header, 1.0, 0.0, 0.76, 2, NULL, "M10", 5 , 1, 1, 0.0},
{ 2400, 0, 0, meisei_header, 1.0, 0.0, 0.70, 2, NULL, "MEISEI", 9 , 0, 1, 0.0},
{ 5800, 0, 0, c34_preheader, 1.5, 0.0, 0.80, 2, NULL, "C34C50", 10 , 0, 1, 0.0}, // C34/C50 2900 Hz tone
{ 9600, 0, 0, imet_preamble, 0.5, 0.0, 0.80, 4, NULL, "IMET", 15 , 1, 0, 0.0}, // IMET1AB=19, IMET1RS=18 (IQ)IMET4=16
{ 9600, 0, 0, imet1ab_header, 1.0, 0.0, 0.80, 2, NULL, "IMET1AB", 19 , 1, 2, 0.0}, // (rs_hdr[idxAB])
{ 9600, 0, 0, imet1rs_header, 0.5, 0.0, 0.80, 2, NULL, "IMET1RS", 18 , 0, 2, 0.0}, // (rs_hdr[idxRS]) IMET4: lpIQ=0 ...
{ 9600, 0, 0, imet1rs_header, 0.5, 0.0, 0.80, 2, NULL, "IMET4", 16 , 1, 0, 0.0} // (rs_hdr[idxI4])
{ 2500, 0, 0, dfm_header, 1.0, 0.0, 0.65, 2, NULL, "DFM9", tn_DFM, 0, 0, 0.0}, // DFM6: -2 ?
{ 4800, 0, 0, rs41_header, 0.5, 0.0, 0.70, 2, NULL, "RS41", tn_RS41, 0, 0, 0.0},
{ 4800, 0, 0, rs92_header, 0.5, 0.0, 0.70, 3, NULL, "RS92", tn_RS92, 0, 0, 0.0}, // RS92NGP: 1680/400=4.2
{ 4800, 0, 0, lms6_header, 1.0, 0.0, 0.70, 2, NULL, "LMS6", tn_LMS6, 0, 0, 0.0}, // lmsX: 7?
{ 9616, 0, 0, mk2a_header, 1.0, 0.0, 0.70, 2, NULL, "MK2LMS", tn_MK2LMS, 1, 2, 0.0}, // Mk2a/LMS6-1680 , --IQ: decimate > 170kHz ...
{ 9616, 0, 0, m10_header, 1.0, 0.0, 0.76, 2, NULL, "M10", tn_M10, 1, 1, 0.0}, // M10.tn=5 (baud=9616) , M20.tn=6 (baud=9600)
{ 2400, 0, 0, meisei_header, 1.0, 0.0, 0.70, 2, NULL, "MEISEI", tn_MEISEI, 0, 1, 0.0},
{ 5800, 0, 0, c34_preheader, 1.5, 0.0, 0.80, 2, NULL, "C34C50", tn_C34C50, 0, 1, 0.0}, // C34/C50 2900 Hz tone
{ 9600, 0, 0, imet_preamble, 0.5, 0.0, 0.80, 4, NULL, "IMET", tn_IMET , 1, 0, 0.0}, // IMET1AB=19, IMET1RS=18 (IQ)IMET4=16
{ 9600, 0, 0, imet1ab_header, 1.0, 0.0, 0.80, 2, NULL, "IMET1AB", tn_IMET+4, 1, 2, 0.0}, // (rs_hdr[idxAB])
{ 9600, 0, 0, imet1rs_header, 0.5, 0.0, 0.80, 2, NULL, "IMET1RS", tn_IMET+3, 0, 2, 0.0}, // (rs_hdr[idxRS]) IMET4: lpIQ=0 ...
{ 9600, 0, 0, imet1rs_header, 0.5, 0.0, 0.80, 2, NULL, "IMET4", tn_IMET+1, 1, 0, 0.0}, // (rs_hdr[idxI4])
};
@ -268,7 +291,7 @@ static int getCorrDFT(int K, unsigned int pos, float *maxv, unsigned int *maxvpo
double xnorm = 1.0;
unsigned int mpos = 0;
double dc = 0.0;
float dc = 0.0;
rshd->dc = 0.0;
if (K + rshd->L > N_DFT) return -1;
@ -575,7 +598,7 @@ static int lowpass_init(float f, int taps, float **pws) {
h = (double*)calloc( taps+1, sizeof(double)); if (h == NULL) return -1;
w = (double*)calloc( taps+1, sizeof(double)); if (w == NULL) return -1;
ws = (float*)calloc( taps+1, sizeof(float)); if (ws == NULL) return -1;
ws = (float*)calloc( 2*taps+1, sizeof(float)); if (ws == NULL) return -1;
for (n = 0; n < taps; n++) {
w[n] = 7938/18608.0 - 9240/18608.0*cos(2*M_PI*n/(taps-1)) + 1430/18608.0*cos(4*M_PI*n/(taps-1)); // Blackmann
@ -586,6 +609,9 @@ static int lowpass_init(float f, int taps, float **pws) {
for (n = 0; n < taps; n++) {
ws[n] /= norm; // 1-norm
}
for (n = 0; n < taps; n++) ws[taps+n] = ws[n]; // duplicate/unwrap
*pws = ws;
free(h); h = NULL;
@ -595,7 +621,7 @@ static int lowpass_init(float f, int taps, float **pws) {
}
// struct { int taps; double *ws}
static float complex lowpass(float complex buffer[], ui32_t sample, ui32_t taps, float *ws) {
static float complex lowpass0(float complex buffer[], ui32_t sample, ui32_t taps, float *ws) {
ui32_t n;
double complex w = 0;
for (n = 0; n < taps; n++) {
@ -603,6 +629,16 @@ static float complex lowpass(float complex buffer[], ui32_t sample, ui32_t taps,
}
return (float complex)w;
}
static float complex lowpass(float complex buffer[], ui32_t sample, ui32_t taps, float *ws) {
ui32_t n;
ui32_t s = sample % taps;
double complex w = 0;
for (n = 0; n < taps; n++) {
w += buffer[n]*ws[taps+s-n]; // ws[taps+s-n] = ws[(taps+sample-n)%taps]
}
return (float complex)w;
// symmetry: ws[n] == ws[taps-1-n]
}
static int f32buf_sample(FILE *fp, int inv) {
@ -721,10 +757,9 @@ static int headcmp(int symlen, unsigned int mvp, int inv, rsheader_t *rshd) {
int errs = 0;
int pos;
int step = 1;
char sign = 0;
int len = 0;
double dc = 0.0;
char sign = 0;
float dc = 0.0;
if (option_dc)
{
@ -755,6 +790,61 @@ static int headcmp(int symlen, unsigned int mvp, int inv, rsheader_t *rshd) {
return errs;
}
static ui8_t bits2byte(char *bitstr) {
int i, bit, d, byteval;
int bitpos;
bitpos = 0;
byteval = 0;
d = 1;
for (i = 0; i < 8; i++) {
//bit=*(bitstr+bitpos+i); /* little endian */
bit=*(bitstr+bitpos+7-i); /* big endian */
if (bit == '1') byteval += d;
else /*if ((bit == '0')*/ byteval += 0;
d <<= 1;
}
return byteval & 0xFF;
}
static int hw(ui8_t byte) {
int i;
int d = 0;
for (i = 0; i < 8; i++) {
d += (byte & 1);
byte >>= 1;
}
return d;
}
static ui32_t frm_M10(unsigned int mvp, int inv, rsheader_t *rshd) {
float dc = 0.0;
int pos2;
char bit0 = '0';
char mb[2];
char frmbit[16+1];
ui8_t b[2];
ui32_t bytes;
if (option_dc) dc = rshd->dc;
bit0 = 0x30 + (inv > 0);
for (pos2 = 0; pos2 < 16; pos2 += 1) {
read_bufbit(2, mb, mvp, pos2==0, dc, rshd);
frmbit[pos2] = 0x31 ^ (bit0 ^ mb[0]);
bit0 = mb[0];
}
frmbit[pos2] = '\0';
b[0] = bits2byte(frmbit);
b[1] = bits2byte(frmbit+8);
bytes = (b[0]<<8) | b[1];
return bytes;
}
/* -------------------------------------------------------------------------- */
#define IF_SAMPLE_RATE 48000
@ -1198,7 +1288,7 @@ int main(int argc, char **argv) {
else {
fp = fopen(*argv, "rb");
if (fp == NULL) {
fprintf(stderr, "%s konnte nicht geoeffnet werden\n", *argv);
fprintf(stderr, "error: open %s\n", *argv);
return -50;
}
wavloaded = 1;
@ -1229,7 +1319,7 @@ int main(int argc, char **argv) {
};
for (j = 0; j < Nrs; j++) {
mv[j] = 0;
mv[j] = 0.0;
mv_pos[j] = 0;
mp[j] = 0;
}
@ -1266,7 +1356,22 @@ int main(int argc, char **argv) {
if (mv_pos[j] > mv0_pos[j]) {
herrs = headcmp(1, mv_pos[j], mv[j]<0, rs_hdr+j);
if (herrs < rs_hdr[j].herrs) { // max bit-errors in header
if (herrs < rs_hdr[j].herrs) // max bit-errors in header
{
if ( strncmp(rs_hdr[j].type, "M10", 3) == 0 || strncmp(rs_hdr[j].type, "M20", 3) == 0)
{
ui32_t bytes = frm_M10(mv_pos[j], mv[j]<0, rs_hdr+j);
int len = (bytes >> 8) & 0xFF;
int h = hw(bytes & 0x0F);
if (h < 2 || h == 2 && (bytes&0xF0) == 0x20) {
rs_hdr[j].type = "M20";
rs_hdr[j].tn = tn_M20; // M20: 45 20
}
else {
rs_hdr[j].type = "M10";
rs_hdr[j].tn = tn_M10; // M10: 64 9F , M10+: 64 AF , M10-dop: 64 49 (len > 0x60)
}
}
if ( strncmp(rs_hdr[j].type, "IMET", 4) == 0 ) // ? j == idxIMETs
{
@ -1322,13 +1427,13 @@ int main(int argc, char **argv) {
mv_pos[j] = mv_pos[_j0];
rs_hdr[j].dc = rs_hdr[_j0].dc;
rs_hdr[j].df = rs_hdr[_j0].df;
mv[_j0] = 0;
mv[_j0] = 0.0;
header_found = 1;
}
else mv[j] = 0.0;
}
else { // IMET -> IMET1AB ?
mv[j] = 0;
mv[j] = 0.0;
j = idxAB;
mv_pos[j] = sample_out;
n = 0;