diff --git a/auto_rx/autorx/__init__.py b/auto_rx/autorx/__init__.py index dab78e0..1c189e5 100644 --- a/auto_rx/autorx/__init__.py +++ b/auto_rx/autorx/__init__.py @@ -17,7 +17,7 @@ except ImportError: # MINOR - New sonde type support, other fairly big changes that may result in telemetry or config file incompatability issus. # PATCH - Small changes, or minor feature additions. -__version__ = "1.3.2-beta2" +__version__ = "1.3.2-beta3" # Global Variables diff --git a/auto_rx/autorx/decode.py b/auto_rx/autorx/decode.py index 8b9b8e4..1128dd1 100644 --- a/auto_rx/autorx/decode.py +++ b/auto_rx/autorx/decode.py @@ -465,9 +465,9 @@ class SondeDecoder(object): if self.save_decode_iq: demod_cmd += " tee decode_IQ_%s.bin |" % str(self.device_idx) - demod_cmd += "./fsk_demod --cs16 -b %d -u %d --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) + demod_cmd += "./fsk_demod --cs16 -b %d -u %d -s --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) - decode_cmd = "./rs41mod --ptu --json --bin 2>/dev/null" + decode_cmd = "./rs41mod --ptu --json --softin -i 2>/dev/null" # RS41s transmit pulsed beacons - average over the last 2 frames, and use a peak-hold demod_stats = FSKDemodStats(averaging_time=2.0, peak_hold=True) @@ -520,15 +520,9 @@ class SondeDecoder(object): if self.save_decode_iq: demod_cmd += " tee decode_IQ_%s.bin |" % str(self.device_idx) - demod_cmd += "./fsk_demod --cs16 -b %d -u %d --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) + demod_cmd += "./fsk_demod --cs16 -b %d -u %d -s --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) - decode_cmd = " python ./test/bit_to_samples.py %d %d | sox -t raw -r %d -e unsigned-integer -b 8 -c 1 - -r %d -b 8 -t wav - 2>/dev/null|" % (_output_rate, _baud_rate, _output_rate, _output_rate) - - # Add in tee command to save audio to disk if debugging is enabled. - if self.save_decode_audio: - decode_cmd += " tee decode_%s.wav |" % str(self.device_idx) - - decode_cmd += "./rs92mod -vx -v --crc --ecc --vel --json %s 2>/dev/null" % _rs92_gps_data + decode_cmd = "./rs92mod -vx -v --crc --ecc --vel --json --softin -i %s 2>/dev/null" % _rs92_gps_data # RS92s transmit continuously - average over the last 2 frames, and use a mean demod_stats = FSKDemodStats(averaging_time=2.0, peak_hold=False) @@ -550,15 +544,10 @@ class SondeDecoder(object): if self.save_decode_iq: demod_cmd += " tee decode_IQ_%s.bin |" % str(self.device_idx) - demod_cmd += "./fsk_demod --cs16 -b %d -u %d --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) - - decode_cmd = "" - # Add in tee command to save audio to disk if debugging is enabled. - if self.save_decode_audio: - decode_cmd += " tee decode_%s.wav |" % str(self.device_idx) + demod_cmd += "./fsk_demod --cs16 -b %d -u %d -s --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) # DFM decoder - decode_cmd += "./dfm09mod -vv --ecc --json --dist --auto --bin 2>/dev/null" + decode_cmd = "./dfm09mod -vv --ecc --json --dist --auto --softin -i 2>/dev/null" # DFM sondes transmit continuously - average over the last 2 frames, and use a mean demod_stats = FSKDemodStats(averaging_time=1.0, peak_hold=False) @@ -581,16 +570,10 @@ class SondeDecoder(object): if self.save_decode_iq: demod_cmd += " tee decode_IQ_%s.bin |" % str(self.device_idx) - demod_cmd += "./fsk_demod --cs16 -b %d -u %d --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) - - decode_cmd = " python ./test/bit_to_samples.py %d %d | sox -t raw -r %d -e unsigned-integer -b 8 -c 1 - -r %d -b 8 -t wav - 2>/dev/null| " % (_sdr_rate, _baud_rate, _sdr_rate, _sdr_rate) - - # Add in tee command to save audio to disk if debugging is enabled. - if self.save_decode_audio: - decode_cmd += " tee decode_%s.wav |" % str(self.device_idx) + demod_cmd += "./fsk_demod --cs16 -b %d -u %d -s -p 5 --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) # M10 decoder - decode_cmd += "./m10mod --json --ptu -vvv 2>/dev/null" + decode_cmd = "./m10mod --json --ptu -vvv --softin -i 2>/dev/null" # M10 sondes transmit in short, irregular pulses - average over the last 2 frames, and use a peak hold demod_stats = FSKDemodStats(averaging_time=2.0, peak_hold=True) @@ -611,15 +594,9 @@ class SondeDecoder(object): if self.save_decode_iq: demod_cmd += " tee decode_IQ_%s.bin |" % str(self.device_idx) - demod_cmd += "./fsk_demod --cs16 -b %d -u %d --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) - - decode_cmd = " python ./test/bit_to_samples.py %d %d | sox -t raw -r %d -e unsigned-integer -b 8 -c 1 - -r %d -b 8 -t wav - 2>/dev/null|" % (_output_rate, _baud_rate, _output_rate, _output_rate) - - # Add in tee command to save audio to disk if debugging is enabled. - if self.save_decode_audio: - decode_cmd += " tee decode_%s.wav |" % str(self.device_idx) + demod_cmd += "./fsk_demod --cs16 -b %d -u %d -s --stats=%d 2 %d %d - -" % (_lower, _upper, _stats_rate, _sdr_rate, _baud_rate) - decode_cmd += "./lms6Xmod --json 2>/dev/null" + decode_cmd = "./lms6Xmod --json --softin -i 2>/dev/null" # LMS sondes transmit continuously - average over the last 2 frames, and use a mean demod_stats = FSKDemodStats(averaging_time=2.0, peak_hold=False) diff --git a/auto_rx/test/plot_per.py b/auto_rx/test/plot_per.py index d865407..cac9bab 100644 --- a/auto_rx/test/plot_per.py +++ b/auto_rx/test/plot_per.py @@ -28,11 +28,13 @@ CSV_DIR = "./results/" # 'M10': {'csv':'m10_rtlfm.txt', 'packets': 120, 'color': 'C3'}, # } + sonde_types = { - 'RS41': {'csv':'rs41_fsk_demod.txt', 'packets': 120, 'color': 'C0'}, - 'RS92': {'csv':'rs92_fsk_demod.txt', 'packets': 120, 'color': 'C1'}, - 'DFM09': {'csv':'dfm_fsk_demod.txt', 'packets': 96, 'color': 'C2'}, - 'M10': {'csv':'m10_fsk_demod.txt', 'packets': 120, 'color': 'C3'}, + 'RS41': {'csv':'rs41_fsk_demod_soft.txt', 'packets': 118, 'color': 'C0'}, + 'RS92': {'csv':'rs92_fsk_demod_soft.txt', 'packets': 120, 'color': 'C1'}, + 'DFM09': {'csv':'dfm_fsk_demod_soft.txt', 'packets': 96, 'color': 'C2'}, + 'M10': {'csv':'m10_fsk_demod_soft.txt', 'packets': 120, 'color': 'C3'}, + 'LMS6-400': {'csv':'lms6-400_fsk_demod_soft.txt', 'packets': 120, 'color': 'C4'}, } @@ -60,7 +62,7 @@ def read_csv(filename): # Sort list of sonde types. -_types = sonde_types.keys() +_types = list(sonde_types.keys()) _types.sort() @@ -80,5 +82,5 @@ plt.legend() plt.grid() plt.ylabel("Packet Error Rate") plt.xlabel("Eb/No (dB)") -plt.title("auto_rx Decode Chain Performance - fsk_demod test 1") +plt.title("auto_rx Decode Chain Performance - fsk_demod") plt.show() \ No newline at end of file diff --git a/auto_rx/test/test_demod.py b/auto_rx/test/test_demod.py index 582c187..f07019d 100644 --- a/auto_rx/test/test_demod.py +++ b/auto_rx/test/test_demod.py @@ -142,7 +142,7 @@ processing_type = { }, 'm10_fsk_demod': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 1.0016666 -c | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96160 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null| ", + 'demod' : "| csdr shift_addition_cc 0.125 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 0.50083333333 -c | ../fsk_demod --cs16 -b 1 -u 45000 -p 5 --stats=5 2 48080 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null| ", 'decode': "tee test.wav | ../m10mod --json -vvv 2>/dev/null", # Count the number of telemetry lines. "post_process" : "| grep aprsid | wc -l", @@ -188,77 +188,59 @@ processing_type = { # No low-SNR samples for this sonde available yet. 'files' : "./generated/lms6-1680*" }, - - ## Tests for the updated fsk_demod (2020-06) - 'rs41_fsk_demod_new': { + 'rs41_fsk_demod_soft': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.125 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 0.500 | ../fsk_demod --cs16 -b 1 -u 24000 --stats=5 2 48000 4800 - - 2>stats.txt |", + 'demod' : "| csdr shift_addition_cc 0.125 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 0.500 | ../fsk_demod --cs16 -b 1 -u 24000 -s --stats=5 2 48000 4800 - - 2>stats.txt |", # Decode using rs41ecc - 'decode': "../rs41mod --ecc --ptu --crc --bin --json 2>/dev/null", + 'decode': "../rs41mod --ecc --ptu --crc --softin -i --json 2>/dev/null", # Count the number of telemetry lines. "post_process" : " | grep frame | wc -l", 'files' : "./generated/rs41*" }, # # RS92 Decoding - 'rs92_fsk_demod_new': { + 'rs92_fsk_demod_soft': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 -s --stats=5 2 96000 4800 - - 2>stats.txt |", # Decode using rs41ecc - 'decode': "../rs92mod -vx -v --crc --ecc --vel 2>/dev/null", + 'decode': "../rs92mod -vx -v --crc --ecc --vel --softin -i 2>/dev/null", # Count the number of telemetry lines. "post_process" : " | grep M2513116 | wc -l", 'files' : "./generated/rs92*" }, - 'm10_fsk_demod_new': { + 'm10_fsk_demod_soft': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 1.0016666 -c | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96160 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null| ", - 'decode': "tee test.wav | ../m10mod --json -vvv 2>/dev/null", + 'demod' : "| csdr shift_addition_cc 0.125 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 0.50083333333 -c | ../fsk_demod --cs16 -b 1 -p 5 -u 23000 -s --stats=5 2 48080 9616 - - 2>stats.txt |", + 'decode': "../m10mod --json --softin -i -vvv 2>/dev/null", # Count the number of telemetry lines. "post_process" : "| grep aprsid | wc -l", 'files' : "./generated/m10*" }, - 'dfm_fsk_demod_new': { + 'dfm_fsk_demod_soft': { # cat ./generated/dfm09_96k_float_15.0dB.bin | csdr shift_addition_cc 0.25000 2>/dev/null | csdr convert_f_s16 | #./tsrc - - 1.041666 | ../fsk_demod --cs16 -b 1 -u 45000 2 100000 2500 - - 2>/dev/null | #python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| #../dfm09ecc -vv --json --dist --auto - 'demod': '| csdr shift_addition_cc 0.125000 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 0.5208| ../fsk_demod --cs16 -b 1250 -u 23750 --stats=5 2 50000 2500 - - 2>stats.txt |',#' python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| ', - 'decode': '../dfm09mod -vv --json --dist --auto --bin 2>/dev/null', + 'demod': '| csdr shift_addition_cc 0.125000 2>/dev/null | csdr convert_f_s16 | ../tsrc - - 0.5208| ../fsk_demod --cs16 -b 1250 -u 23750 -s --stats=5 2 50000 2500 - - 2>stats.txt |',#' python ./bit_to_samples.py 50000 2500 | sox -t raw -r 50k -e unsigned-integer -b 8 -c 1 - -r 50000 -b 8 -t wav - 2>/dev/null| ', + 'decode': '../dfm09mod -vv --json --dist --auto --softin -i 2>/dev/null', "post_process" : " | grep frame | wc -l", # ECC #"post_process" : "| grep -o '\[OK\]' | wc -l", # No ECC 'files' : "./generated/dfm*.bin" }, # LMS6-400 Decoding - 'lms6-400_fsk_demod_new': { + 'lms6-400_fsk_demod_soft': { # Shift up to ~24 khz, and then pass into fsk_demod. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 --stats=5 2 96000 4800 - - 2>stats.txt | python ./bit_to_samples.py 48000 4800 | sox -t raw -r 48k -e unsigned-integer -b 8 -c 1 - -r 48000 -b 8 -t wav - 2>/dev/null|", + 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ../fsk_demod --cs16 -b 1 -u 45000 -s --stats=5 2 96000 4800 - - 2>stats.txt |", # Decode using rs41ecc - 'decode': "../lms6Xmod --json 2>/dev/null", + 'decode': "../lms6Xmod --json --softin -i 2>/dev/null", # Count the number of telemetry lines. "post_process" : "| grep frame | wc -l", 'files' : "./generated/lms6-400*", }, - - 'lms6-1680_fsk_demod_new': { - # This is a weird one. - # The baud rate is ~9616 Baud, but the deviation is *huge* (~170 kHz occupied bandwidth). - # The recording bandwidth needs to be correspondingly huge, with ~480 kHz sample rate required to capture the signal. - # We need to resample up to a multiple of 9616 Hz to be able to get fsk_demod to decode. - # fsk_demod does not decode these types reliably at the moment. - 'demod' : "| csdr shift_addition_cc 0.25 2>/dev/null | csdr convert_f_s16 | ./tsrc - - 1.00166666 | ../fsk_demod --cs16 -b 5000 -u 230000 --stats=5 2 480800 9616 - - 2>stats.txt | python ./bit_to_samples.py 57696 9616 | sox -t raw -r 57696 -e unsigned-integer -b 8 -c 1 - -r 57696 -b 8 -t wav - 2>/dev/null|", - - # Decode using rs41ecc - 'decode': "../mk2a_lms1680 -i --json 2>/dev/null", - # Count the number of telemetry lines. - "post_process" : " | grep frame | wc -l", - # No low-SNR samples for this sonde available yet. - 'files' : "./generated/lms6-1680*" - }, } @@ -721,7 +703,7 @@ if __name__ == "__main__": sys.exit(1) - batch_modes = ['dfm_fsk_demod', 'rs41_fsk_demod', 'm10_fsk_demod', 'rs92_fsk_demod', 'lms6-400_fsk_demod', 'imet4_rtlfm'] + batch_modes = ['dfm_fsk_demod_soft', 'rs41_fsk_demod_soft', 'm10_fsk_demod_soft', 'rs92_fsk_demod_soft', 'lms6-400_fsk_demod_soft']#, 'imet4_rtlfm'] if args.batch: for _mode in batch_modes: diff --git a/demod/mod/README.md b/demod/mod/README.md index 2456c54..3cd0687 100644 --- a/demod/mod/README.md +++ b/demod/mod/README.md @@ -59,4 +59,9 @@ alternative decoders using cross-correlation for better header-synchronization 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. + soft input:
+ Option `--softin` expects float32 symbols as input, with `s>0` corresponding to `bit=1`.
+ (remark/caution: often soft bits are defined as `bit=0 -> s=+1` and `bit=1 -> s=-1` such that the identity element `0` + for addition mod 2 corresponds to the identity element `+1` for multiplication.) + diff --git a/demod/mod/demod_mod.c b/demod/mod/demod_mod.c index 1d6504d..a432647 100644 --- a/demod/mod/demod_mod.c +++ b/demod/mod/demod_mod.c @@ -24,6 +24,8 @@ /* ------------------------------------------------------------------------------------ */ +#ifndef EXT_FSK + static void raw_dft(dft_t *dft, float complex *Z) { int s, l, l2, i, j, k; float complex w1, w2, T; @@ -126,7 +128,7 @@ static int dft_window(dft_t *dft, int w) { dft->win[n] = 0.5 * ( 1.0 - cos(2*M_PI*n/(float)(dft->N2-1)) ); break ; case 2: // Hamming - dft->win[n] = 25/46.0 + (1.0 - 25/46.0)*cos(2*M_PI*n / (float)(dft->N2-1)); + dft->win[n] = 25/46.0 - (1.0 - 25/46.0)*cos(2*M_PI*n / (float)(dft->N2-1)); break ; case 3: // Blackmann dft->win[n] = 7938/18608.0 @@ -942,6 +944,96 @@ int read_softbit(dsp_t *dsp, hsbit_t *shb, int inv, int ofs, int pos, float l, i return 0; } +int read_softbit2p(dsp_t *dsp, hsbit_t *shb, int inv, int ofs, int pos, float l, int spike, hsbit_t *shb1) { +// symlen==2: manchester2 10->0,01->1: 2.bit + + float sample, sample1; + float avg; + float ths = 0.5, scale = 0.27; + + double sum = 0.0, sum1 = 0.0; + double mid; + //double l = 1.0; + + double bg = pos*dsp->symlen*dsp->sps; + + double dc = 0.0; + + ui8_t bit = 0, bit1 = 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]; + sample1 = dsp->bufs[(dsp->sample_out-dsp->buffered + ofs-1 + 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; + sample1 -= dc; + + if (l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) { + sum -= sample; + sum1 -= sample1; + } + + 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]; + sample1 = dsp->bufs[(dsp->sample_out-dsp->buffered + ofs-1 + 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; + sample1 -= dc; + + if (l < 0 || (mid-l < dsp->sc && dsp->sc < mid+l)) { + sum += sample; + sum1 += sample1; + } + + dsp->sc++; + } while (dsp->sc < bg); // n < dsp->sps + + + if (sum >= 0) bit = 1; + else bit = 0; + shb->hb = bit; + shb->sb = (float)sum; + + if (sum1 >= 0) bit1 = 1; + else bit1 = 0; + shb1->hb = bit1; + shb1->sb = (float)sum1; + + return 0; +} + /* -------------------------------------------------------------------------- */ #define IF_SAMPLE_RATE 48000 @@ -1275,12 +1367,6 @@ int free_buffers(dsp_t *dsp) { /* ------------------------------------------------------------------------------------ */ -ui32_t get_sample(dsp_t *dsp) { - return dsp->sample_out; -} - -/* ------------------------------------------------------------------------------------ */ - int find_header(dsp_t *dsp, float thres, int hdmax, int bitofs, int opt_dc) { ui32_t k = 0; @@ -1345,3 +1431,145 @@ int find_header(dsp_t *dsp, float thres, int hdmax, int bitofs, int opt_dc) { return EOF; } +/* ------------------------------------------------------------------------------------ */ + + +#else +// external FSK demod: read float32 soft symbols + +float read_wav_header(pcm_t *pcm, FILE *fp) {} +int f32buf_sample(dsp_t *dsp, int inv) {} +int read_slbit(dsp_t *dsp, int *bit, int inv, int ofs, int pos, float l, int spike) {} +int read_softbit(dsp_t *dsp, hsbit_t *shb, int inv, int ofs, int pos, float l, int spike) {} +int read_softbit2p(dsp_t *dsp, hsbit_t *shb, int inv, int ofs, int pos, float l, int spike, hsbit_t *shb1) {} + +int init_buffers(dsp_t *dsp) {} +int free_buffers(dsp_t *dsp) {} + +int find_header(dsp_t *dsp, float thres, int hdmax, int bitofs, int opt_dc) {} + +#endif + + +static float cmp_hdb(hdb_t *hdb) { // bit-errors? + int i, j; + int headlen = hdb->len; + int berrs1 = 0, berrs2 = 0; + + i = 0; + j = hdb->bufpos; + while (i < headlen) { + if (j < 0) j = headlen-1; + if (hdb->buf[j] != hdb->hdr[headlen-1-i]) berrs1 += 1; + j--; + i++; + } + + i = 0; + j = hdb->bufpos; + while (i < headlen) { + if (j < 0) j = headlen-1; + if ((hdb->buf[j]^0x01) != hdb->hdr[headlen-1-i]) berrs2 += 1; + j--; + i++; + } + + if (berrs2 < berrs1) return (-headlen+berrs2)/(float)headlen; + else return ( headlen-berrs1)/(float)headlen; + + return 0; +} + +int find_binhead(FILE *fp, hdb_t *hdb, float *score) { + int bit; + int headlen = hdb->len; + float mv; + + //*score = 0.0; + + while ( (bit = fgetc(fp)) != EOF ) + { + bit &= 1; + + hdb->bufpos = (hdb->bufpos+1) % headlen; + hdb->buf[hdb->bufpos] = 0x30 | bit; // Ascii + + mv = cmp_hdb(hdb); + if ( fabs(mv) > hdb->thb ) { + *score = mv; + return 1; + } + } + + return EOF; +} + +static float corr_softhdb(hdb_t *hdb) { // max score in window probably not needed + int i, j; + int headlen = hdb->len; + double sum = 0.0; + double normx = 0.0, + normy = 0.0; + float x, y; + + i = 0; + j = hdb->bufpos + 1; + + while (i < headlen) { + if (j >= headlen) j = 0; + x = hdb->sbuf[j]; + y = 2.0*(hdb->hdr[i]&0x1) - 1.0; + sum += y * hdb->sbuf[j]; + normx += x*x; + normy += y*y; + j++; + i++; + } + sum /= sqrt(normx*normy); + + return sum; +} + +int f32soft_read(FILE *fp, float *s) { + unsigned int word = 0; + short *b = (short*)&word; + float *f = (float*)&word; + int bps = 32; + + if (fread( &word, bps/8, 1, fp) != 1) return EOF; + + if (bps == 32) { + *s = *f; + } + else { + if (bps == 8) { *b -= 128; } + *s = *b/128.0; + if (bps == 16) { *s /= 256.0; } + } + + return 0; +} + +int find_softbinhead(FILE *fp, hdb_t *hdb, float *score) { + int headlen = hdb->len; + float sbit; + float mv; + + //*score = 0.0; + + while ( f32soft_read(fp, &sbit) != EOF ) + { + hdb->bufpos = (hdb->bufpos+1) % headlen; + hdb->sbuf[hdb->bufpos] = sbit; + + mv = corr_softhdb(hdb); + + if ( fabs(mv) > hdb->ths ) { + *score = mv; + return 1; + } + } + + return EOF; +} + diff --git a/demod/mod/demod_mod.h b/demod/mod/demod_mod.h index bfe3a14..26934fd 100644 --- a/demod/mod/demod_mod.h +++ b/demod/mod/demod_mod.h @@ -144,16 +144,30 @@ typedef struct { } hsbit_t; +typedef struct { + char *hdr; + char *buf; + float *sbuf; + int len; + int bufpos; + float thb; + float ths; +} hdb_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 read_softbit(dsp_t *, hsbit_t *, int, int, int, float, int); +int read_softbit2p(dsp_t *dsp, hsbit_t *shb, int inv, int ofs, int pos, float l, int spike, hsbit_t *shb1); int init_buffers(dsp_t *); int free_buffers(dsp_t *); -ui32_t get_sample(dsp_t *); - int find_header(dsp_t *, float, int, int, int); +int f32soft_read(FILE *fp, float *s); +int find_binhead(FILE *fp, hdb_t *hdb, float *score); +int find_softbinhead(FILE *fp, hdb_t *hdb, float *score); + + diff --git a/demod/mod/dfm09mod.c b/demod/mod/dfm09mod.c index d5372d5..5f7b130 100644 --- a/demod/mod/dfm09mod.c +++ b/demod/mod/dfm09mod.c @@ -52,6 +52,12 @@ typedef struct { ui32_t chX[2]; } sn_t; +typedef struct { + ui32_t prn; // SVs used (PRN) + float dMSL; // Alt_MSL - Alt_ellipsoid = -N = - geoid_height = ellipsoid - geoid + ui8_t nSV; // numSVs used +} gpsdat_t; + #define BITFRAME_LEN 280 typedef struct { @@ -74,6 +80,7 @@ typedef struct { pcksts_t pck[9]; option_t option; int ptu_out; + gpsdat_t gps; } gpx_t; @@ -294,6 +301,9 @@ static int dat_out(gpx_t *gpx, ui8_t *dat_bits, int ec) { } } + // GPS data + // SiRF msg ID 41: Geodetic Navigation Data + if (fr_id == 0) { //start = 0x1000; frnr = bits2val(dat_bits+24, 8); @@ -301,7 +311,8 @@ static int dat_out(gpx_t *gpx, ui8_t *dat_bits, int ec) { } if (fr_id == 1) { - // 00..31: ? GPS-Sats in Sicht? + // 00..31: GPS-Sats in solution (bitmap) + gpx->gps.prn = bits2val(dat_bits, 32); // SV/PRN used msek = bits2val(dat_bits+32, 16); // UTC (= GPS - 18sec ab 1.1.2017) gpx->sek = msek/1000.0; } @@ -328,6 +339,8 @@ static int dat_out(gpx_t *gpx, ui8_t *dat_bits, int ec) { } if (fr_id == 5) { + short dMSL = bits2val(dat_bits, 16); + gpx->gps.dMSL = dMSL/1e2; } if (fr_id == 6) { // sat data @@ -342,6 +355,7 @@ static int dat_out(gpx_t *gpx, ui8_t *dat_bits, int ec) { gpx->tag = bits2val(dat_bits+16, 5); gpx->std = bits2val(dat_bits+21, 5); gpx->min = bits2val(dat_bits+26, 6); + gpx->gps.nSV = bits2val(dat_bits+32, 8); } ret = fr_id; @@ -733,6 +747,16 @@ static void print_gpx(gpx_t *gpx) { } printf("\n"); + if (gpx->option.sat) { + printf(" "); + printf(" dMSL: %+.2f", gpx->gps.dMSL); // MSL = alt + gps.dMSL + printf(" sats: %d", gpx->gps.nSV); + printf(" ("); + for (j = 0; j < 32; j++) { if ((gpx->gps.prn >> j)&1) printf(" %02d", j+1); } + printf(" )"); + printf("\n"); + } + if (gpx->option.jsn && jsonout) { // JSON Buffer to store sonde ID @@ -861,67 +885,6 @@ static int print_frame(gpx_t *gpx) { /* -------------------------------------------------------------------------- */ -// header bit buffer -typedef struct { - char *hdr; - char *buf; - char len; - int bufpos; - float ths; -} hdb_t; - -static float cmp_hdb(hdb_t *hdb) { // bit-errors? - int i, j; - int headlen = hdb->len; - int berrs1 = 0, berrs2 = 0; - - i = 0; - j = hdb->bufpos; - while (i < headlen) { - if (j < 0) j = headlen-1; - if (hdb->buf[j] != hdb->hdr[headlen-1-i]) berrs1 += 1; - j--; - i++; - } - - i = 0; - j = hdb->bufpos; - while (i < headlen) { - if (j < 0) j = headlen-1; - if ((hdb->buf[j]^0x01) != hdb->hdr[headlen-1-i]) berrs2 += 1; - j--; - i++; - } - if (berrs2 < berrs1) return (-headlen+berrs2)/(float)headlen; - else return ( headlen-berrs1)/(float)headlen; - - return 0; -} - -static int find_binhead(FILE *fp, hdb_t *hdb, float *score) { - int bit; - int headlen = hdb->len; - float mv; - - //*score = 0.0; - - while ( (bit = fgetc(fp)) != EOF ) - { - bit &= 1; - - hdb->bufpos = (hdb->bufpos+1) % headlen; - hdb->buf[hdb->bufpos] = 0x30 | bit; // Ascii - - mv = cmp_hdb(hdb); - if ( fabs(mv) > hdb->ths ) { - *score = mv; - return 1; - } - } - - return EOF; -} - int main(int argc, char **argv) { @@ -937,6 +900,7 @@ int main(int argc, char **argv) { int option_lp = 0; int option_dc = 0; int option_bin = 0; + int option_softin = 0; int option_json = 0; // JSON blob output (for auto_rx) int option_pcmraw = 0; int wavloaded = 0; @@ -1018,12 +982,13 @@ int main(int argc, char **argv) { else if ( (strcmp(*argv, "--spike") == 0) ) { spike = 1; } - else if ( (strcmp(*argv, "--auto") == 0) ) { option_auto = 1; } - else if (strcmp(*argv, "--bin") == 0) { option_bin = 1; } // bit/byte binary input - else if ( (strcmp(*argv, "--dist") == 0) ) { option_dist = 1; option_ecc = 1; } - else if ( (strcmp(*argv, "--json") == 0) ) { option_json = 1; option_ecc = 1; } - else if ( (strcmp(*argv, "--ch2") == 0) ) { sel_wavch = 1; } // right channel (default: 0=left) - else if ( (strcmp(*argv, "--ths") == 0) ) { + else if (strcmp(*argv, "--auto") == 0) { option_auto = 1; } + else if (strcmp(*argv, "--bin") == 0) { option_bin = 1; } // bit/byte binary input + else if (strcmp(*argv, "--softin") == 0) { option_softin = 1; } // float32 soft input + else if (strcmp(*argv, "--dist") == 0) { option_dist = 1; option_ecc = 1; } + else if (strcmp(*argv, "--json") == 0) { option_json = 1; option_ecc = 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); @@ -1058,6 +1023,7 @@ int main(int argc, char **argv) { option_min = 1; } else if (strcmp(*argv, "--dbg") == 0) { gpx.option.dbg = 1; } + else if (strcmp(*argv, "--sat") == 0) { gpx.option.sat = 1; } else if (strcmp(*argv, "-") == 0) { int sample_rate = 0, bits_sample = 0, channels = 0; ++argv; @@ -1122,11 +1088,17 @@ int main(int argc, char **argv) { gpx.option.dst = option_dist; gpx.option.jsn = option_json; - headerlen = strlen(dfm_rawheader); - if (!option_bin) { + #ifdef EXT_FSK + if (!option_bin && !option_softin) { + option_softin = 1; + fprintf(stderr, "reading float32 soft symbols\n"); + } + #endif + + if (!option_bin && !option_softin) { if (option_iq == 0 && option_pcmraw) { fclose(fp); @@ -1174,38 +1146,48 @@ int main(int argc, char **argv) { if ( dsp.sps < 8 ) { fprintf(stderr, "note: sample rate low\n"); } + + + k = init_buffers(&dsp); + if ( k < 0 ) { + fprintf(stderr, "error: init buffers\n"); + return -1; + } + + bitofs += shift; } else { + if (option_bin && option_softin) option_bin = 0; // init circular header bit buffer hdb.hdr = dfm_rawheader; hdb.len = strlen(dfm_rawheader); - hdb.ths = 1.0 - 2.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen // max 1.1 !! + hdb.thb = 1.0 - 2.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen // max 1.1 !! hdb.bufpos = -1; hdb.buf = calloc(hdb.len, sizeof(char)); if (hdb.buf == NULL) { fprintf(stderr, "error: malloc\n"); return -1; } + hdb.ths = 0.7; // caution/test false positive + hdb.sbuf = calloc(hdb.len, sizeof(float)); + if (hdb.sbuf == NULL) { + fprintf(stderr, "error: malloc\n"); + return -1; + } } - k = init_buffers(&dsp); - if ( k < 0 ) { - fprintf(stderr, "error: init buffers\n"); - return -1; - }; - - - bitofs += shift; - - while ( 1 ) { if (option_bin) { // aka find_binrawhead() header_found = find_binhead(fp, &hdb, &_mv); // symbols or bits? hdrcnt += nfrms; } - else { // FM-audio: + else if (option_softin) { + header_found = find_softbinhead(fp, &hdb, &_mv); + hdrcnt += nfrms; + } + else { //2 (false positive) // FM-audio: header_found = find_header(&dsp, thres, 2, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0 _mv = dsp.mv; } @@ -1227,7 +1209,7 @@ int main(int argc, char **argv) { frm = 0; while ( frm < nfrms ) { // nfrms=1,2,4,8 - if (option_bin) { + if (option_bin || option_softin) { gpx._frmcnt = hdrcnt + frm; } else { @@ -1247,19 +1229,28 @@ int main(int argc, char **argv) { hsbit.sb = 2*hsbit.hb - 1; } } + else if (option_softin) { + float s1 = 0.0; + float s2 = 0.0; + float s = 0.0; + bitQ = f32soft_read(fp, &s1); + if (bitQ != EOF) { + bitQ = f32soft_read(fp, &s2); + if (bitQ != EOF) { + s = s2-s1; // integrate both symbols // only 2nd Manchester symbol: s2 + hsbit.sb = s; + hsbit.hb = (s>=0.0); + } + } + } else { - 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 { - bitQ = read_softbit(&dsp, &hsbit, 0, bitofs, bitpos, -1, spike); - } + float bl = -1; + if (option_iq >= 2) spike = 0; + if (option_iq > 2) bl = 4.0; + bitQ = read_softbit(&dsp, &hsbit, 0, bitofs, bitpos, bl, spike); // symlen=2 // optional: // normalize soft bit s_j by // rhsbit.sb /= dsp._spb+1; // all samples in [-1,+1] - } if ( bitQ == EOF ) { frm = nfrms; break; } // liest 2x EOF @@ -1285,12 +1276,12 @@ int main(int argc, char **argv) { pos = headerlen; } - if (!option_bin) free_buffers(&dsp); + + if (!option_bin && !option_softin) free_buffers(&dsp); else { if (hdb.buf) { free(hdb.buf); hdb.buf = NULL; } } - fclose(fp); return 0; diff --git a/demod/mod/lms6Xmod.c b/demod/mod/lms6Xmod.c index 290db02..2632790 100644 --- a/demod/mod/lms6Xmod.c +++ b/demod/mod/lms6Xmod.c @@ -3,6 +3,10 @@ * LMS6 * (403 MHz) * + * soft decision test: + * IQ-decoding: --vit2 (soft decision) better performance (low dB) + * FM-decoding: --vit1 (hard decision) better than --vit2 + * * sync header: correlation/matched filter * files: lms6Xmod.c demod_mod.c demod_mod.h bch_ecc_mod.c bch_ecc_mod.h * compile, either (a) or (b): @@ -16,7 +20,8 @@ * * usage: * ./lms6Xmod --vit --ecc - * ( --vit recommended) + * ./lms6Xmod --vit2 --ecc --IQ 0.0 + * ( --vit/--vit2 recommended) * author: zilog80 */ @@ -116,12 +121,11 @@ typedef struct { ui8_t bIn; ui8_t codeIn; ui8_t prevState; // 0..M=64 - int w; // > 255 : if (w>250): w=250 ? - //float sw; + float w; } states_t; typedef struct { - char rawbits[RAWBITFRAME_LEN+OVERLAP*BITS*2 +8]; + hsbit_t rawbits[RAWBITFRAME_LEN+OVERLAP*BITS*2 +8]; states_t state[RAWBITFRAME_LEN+OVERLAP +8][M]; states_t d[N]; } VIT_t; @@ -138,7 +142,7 @@ typedef struct { double lat; double lon; double alt; double vH; double vD; double vV; double vE; double vN; double vU; - char blk_rawbits[RAWBITBLOCK_LEN+SYNC_LEN*BITS*2 +9]; + hsbit_t blk_rawbits[RAWBITBLOCK_LEN+SYNC_LEN*BITS*2 +9]; ui8_t frame[FRM_LEN]; // = { 0x24, 0x54, 0x00, 0x00}; // dataheader int frm_pos; // ecc_blk <-> frm_blk int sf6; @@ -213,12 +217,16 @@ static int vit_initCodes(gpx_t *gpx) { return 0; } -static int vit_dist(int c, char *rc) { - return (((c>>1)^rc[0])&1) + ((c^rc[1])&1); +static float vit_dist2(int c, hsbit_t *rc) { + int c0 = 2*((c>>1) & 1)-1; // {0,1} -> {-1,+1} + int c1 = 2*(c & 1)-1; + float d2 = (c0-rc[0].sb)*(c0-rc[0].sb) + (c1-rc[1].sb)*(c1-rc[1].sb); + return d2; } -static int vit_start(VIT_t *vit, char *rc) { - int t, m, j, c, d; +static int vit_start(VIT_t *vit, hsbit_t *rc) { + int t, m, j, c; + float d; t = L-1; m = M; @@ -236,7 +244,7 @@ static int vit_start(VIT_t *vit, char *rc) { c = vit_code[j]; vit->state[t][j].bIn = j % 2; vit->state[t][j].codeIn = c; - d = vit_dist( c, rc+2*(t-1) ); + d = vit_dist2( c, rc+2*(t-1) ); vit->state[t][j].w = vit->state[t-1][vit->state[t][j].prevState].w + d; } m *= 2; @@ -245,7 +253,7 @@ static int vit_start(VIT_t *vit, char *rc) { return t; } -static int vit_next(VIT_t *vit, int t, char *rc) { +static int vit_next(VIT_t *vit, int t, hsbit_t *rc) { int b, nstate; int j, index; @@ -255,7 +263,7 @@ static int vit_next(VIT_t *vit, int t, char *rc) { vit->d[nstate].bIn = b; vit->d[nstate].codeIn = vit_code[nstate]; vit->d[nstate].prevState = j; - vit->d[nstate].w = vit->state[t][j].w + vit_dist( vit->d[nstate].codeIn, rc ); + vit->d[nstate].w = vit->state[t][j].w + vit_dist2( vit->d[nstate].codeIn, rc ); } } @@ -272,11 +280,11 @@ static int vit_next(VIT_t *vit, int t, char *rc) { static int vit_path(VIT_t *vit, int j, int t) { int c; - vit->rawbits[2*t] = '\0'; + vit->rawbits[2*t].hb = '\0'; while (t > 0) { c = vit->state[t][j].codeIn; - vit->rawbits[2*t -2] = 0x30 + ((c>>1) & 1); - vit->rawbits[2*t -1] = 0x30 + (c & 1); + vit->rawbits[2*t -2].hb = 0x30 + ((c>>1) & 1); + vit->rawbits[2*t -1].hb = 0x30 + (c & 1); j = vit->state[t][j].prevState; t--; } @@ -284,13 +292,20 @@ static int vit_path(VIT_t *vit, int j, int t) { return 0; } -static int viterbi(VIT_t *vit, char *rc) { +static int hbstr_len(hsbit_t *hsbit) { + int len = 0; + while (hsbit[len].hb) len++; + return len; +} + +static int viterbi(VIT_t *vit, hsbit_t *rc) { int t, tmax; - int j, j_min, w_min; + int j, j_min; + float w_min; vit_start(vit, rc); - tmax = strlen(rc)/2; + tmax = hbstr_len(rc)/2; for (t = L-1; t < tmax; t++) { @@ -315,15 +330,15 @@ static int viterbi(VIT_t *vit, char *rc) { // ------------------------------------------------------------------------ -static int deconv(char* rawbits, char *bits) { +static int deconv(hsbit_t *rawbits, char *bits) { int j, n, bitA, bitB; - char *p; + hsbit_t *p; int len; int errors = 0; int m = L-1; - len = strlen(rawbits); + len = hbstr_len(rawbits); for (j = 0; j < m; j++) bits[j] = '0'; n = 0; while ( 2*(m+n) < len ) { @@ -333,10 +348,10 @@ static int deconv(char* rawbits, char *bits) { bitA ^= (bits[n+j]&1) & (polyA[j]&1); bitB ^= (bits[n+j]&1) & (polyB[j]&1); } - if ( (bitA^(p[0]&1))==(polyA[m]&1) && (bitB^(p[1]&1))==(polyB[m]&1) ) bits[n+m] = '1'; - else if ( (bitA^(p[0]&1))==0 && (bitB^(p[1]&1))==0 ) bits[n+m] = '0'; + if ( (bitA^(p[0].hb&1))==(polyA[m]&1) && (bitB^(p[1].hb&1))==(polyB[m]&1) ) bits[n+m] = '1'; + else if ( (bitA^(p[0].hb&1))==0 && (bitB^(p[1].hb&1))==0 ) bits[n+m] = '0'; else { - if ( (bitA^(p[0]&1))!=(polyA[m]&1) && (bitB^(p[1]&1))==(polyB[m]&1) ) bits[n+m] = 0x39; + if ( (bitA^(p[0].hb&1))!=(polyA[m]&1) && (bitB^(p[1].hb&1))==(polyB[m]&1) ) bits[n+m] = 0x39; else bits[n+m] = 0x38; errors = n; break; @@ -795,7 +810,7 @@ static void proc_frame(gpx_t *gpx, int len) { ui8_t block_bytes[FRAME_LEN+8]; ui8_t rs_cw[rs_N]; char frame_bits[BITFRAME_LEN+OVERLAP*BITS +8]; // init L-1 bits mit 0 - char *rawbits = NULL; + hsbit_t *rawbits = NULL; int i, j; int err = 0; int errs = 0; @@ -804,13 +819,17 @@ static void proc_frame(gpx_t *gpx, int len) { if ((len % 8) > 4) { - while (len % 8) gpx->blk_rawbits[len++] = '0'; + while (len % 8) { + gpx->blk_rawbits[len].hb = '0'; + gpx->blk_rawbits[len].sb = -1; + len++; + } } - gpx->blk_rawbits[len] = '\0'; + gpx->blk_rawbits[len].hb = '\0'; flen = len / (2*BITS); - if (gpx->option.vit == 1) { + if (gpx->option.vit) { viterbi(gpx->vit, gpx->blk_rawbits); rawbits = gpx->vit->rawbits; } @@ -922,6 +941,8 @@ static void proc_frame(gpx_t *gpx, int len) { } +/* -------------------------------------------------------------------------- */ + int main(int argc, char **argv) { @@ -930,6 +951,7 @@ int main(int argc, char **argv) { int option_iq = 0; int option_lp = 0; int option_dc = 0; + int option_softin = 0; int option_pcmraw = 0; int wavloaded = 0; int sel_wavch = 0; // audio channel: left @@ -940,7 +962,7 @@ int main(int argc, char **argv) { int k; - int bit, rbit; + hsbit_t hsbit, rhsbit, rhsbit1; int bitpos = 0; int bitQ; int pos; @@ -948,15 +970,17 @@ int main(int argc, char **argv) { int header_found = 0; - float thres = 0.76; + float thres = 0.65; float _mv = 0.0; + float lpIQ_bw = 16e3; + int symlen = 1; int bitofs = 0; int shift = 0; - int bitofs6 = 1; // +1 .. +2 - int bitofsX = 0; // 0 .. +1 + int bitofs6 = 0; // -1 .. +2 + int bitofsX = 0; // -1 .. +1 unsigned int bc = 0; @@ -966,17 +990,21 @@ int main(int argc, char **argv) { pcm_t pcm = {0}; dsp_t dsp = {0}; //memset(&dsp, 0, sizeof(dsp)); -/* + + hdb_t hdb = {0}; + + /* // gpx_t _gpx = {0}; gpx_t *gpx = &_gpx; // stack size ... gpx_t *gpx = NULL; gpx = calloc(1, sizeof(gpx_t)); //memset(gpx, 0, sizeof(gpx_t)); -*/ + */ gpx_t _gpx = {0}; gpx_t *gpx = &_gpx; gpx->auto_detect = 1; gpx->reset_dsp = 0; + #ifdef CYGWIN _setmode(fileno(stdin), _O_BINARY); // _setmode(_fileno(stdin), _O_BINARY); #endif @@ -1010,7 +1038,9 @@ int main(int argc, char **argv) { gpx->option.raw = 1; // bytes - rs_ecc_codewords } else if (strcmp(*argv, "--ecc" ) == 0) { gpx->option.ecc = 1; } // RS-ECC - else if (strcmp(*argv, "--vit" ) == 0) { gpx->option.vit = 1; } // viterbi + else if (strcmp(*argv, "--ecc3") == 0) { gpx->option.ecc = 3; } // RS-ECC + else if (strcmp(*argv, "--vit" ) == 0) { gpx->option.vit = 1; } // viterbi-hard + else if (strcmp(*argv, "--vit2" ) == 0) { gpx->option.vit = 2; } // viterbi-soft else if ( (strcmp(*argv, "--gpsweek") == 0) ) { ++argv; if (*argv) { @@ -1022,8 +1052,9 @@ int main(int argc, char **argv) { else if ( (strcmp(*argv, "-i") == 0) || (strcmp(*argv, "--invert") == 0) ) { option_inv = 1; // nicht noetig } - else if ( (strcmp(*argv, "--ch2") == 0) ) { sel_wavch = 1; } // right channel (default: 0=left) - else if ( (strcmp(*argv, "--ths") == 0) ) { + else if (strcmp(*argv, "--ch2") == 0) { sel_wavch = 1; } // right channel (default: 0=left) + else if (strcmp(*argv, "--softin") == 0) { option_softin = 1; } // float32 soft input + else if (strcmp(*argv, "--ths") == 0) { ++argv; if (*argv) { thres = atof(*argv); @@ -1053,6 +1084,14 @@ int main(int argc, char **argv) { option_iq = 5; } else if (strcmp(*argv, "--lp") == 0) { option_lp = 1; } // IQ lowpass + else if (strcmp(*argv, "--lpbw") == 0) { // IQ lowpass BW / kHz + double bw = 0.0; + ++argv; + if (*argv) bw = atof(*argv); + else return -1; + if (bw > 4.6 && bw < 24.0) lpIQ_bw = bw*1e3; + option_lp = 1; + } else if (strcmp(*argv, "--dc") == 0) { option_dc = 1; } else if (strcmp(*argv, "--min") == 0) { option_min = 1; @@ -1094,71 +1133,113 @@ int main(int argc, char **argv) { if (gpx->option.raw == 4) gpx->option.ecc = 1; // init gpx - memcpy(gpx->blk_rawbits, blk_syncbits, sizeof(blk_syncbits)); memcpy(gpx->frame, frm_sync6, sizeof(frm_sync6)); gpx->frm_pos = 0; // ecc_blk <-> frm_blk gpx->sf6 = 0; gpx->sfX = 0; + //memcpy(gpx->blk_rawbits, blk_syncbits, sizeof(blk_syncbits)); + for (k = 0; k < strlen(blk_syncbits); k++) { // strlen(blk_syncbits)=BLOCKSTART + int hbit = blk_syncbits[k] & 1; + gpx->blk_rawbits[k].hb = hbit + 0x30; + gpx->blk_rawbits[k].sb = 2*hbit-1; + } gpx->option.inv = option_inv; // irrelevant gpx->week = gpsweek; - 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 ) { + #ifdef EXT_FSK + if (!option_softin) { + option_softin = 1; + fprintf(stderr, "reading float32 soft symbols\n"); + } + #endif + + if (!option_softin) { + + if (option_iq == 0 && option_pcmraw) { fclose(fp); - fprintf(stderr, "error: wav header\n"); + fprintf(stderr, "error: raw data not IQ\n"); + return -1; + } + if (option_iq == 0 && gpx->option.vit == 2) { // FM-demodulated data not recommended + gpx->option.vit = 1; // for soft-decoding + fprintf(stderr, "info: soft decoding only for IQ\n"); + } + 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; + } + } + + symlen = 1; + + // 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_RATE6; + dsp.sps = (float)dsp.sr/dsp.br; + dsp.symlen = symlen; + dsp.symhd = 1; + dsp._spb = dsp.sps*symlen; + dsp.hdr = rawheader; + dsp.hdrlen = strlen(rawheader); + dsp.BT = 1.2; // bw/time (ISI) // 1.0..2.0 // BT(lmsX) < BT(lms6) ? -> init_buffers() + dsp.h = 0.9; // 0.95 modulation index + dsp.opt_iq = option_iq; + dsp.opt_lp = option_lp; + dsp.lpIQ_bw = lpIQ_bw; // 16e3; // IF lowpass bandwidth // soft decoding? + dsp.lpFM_bw = 6e3; // 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); + } + + //headerlen = dsp.hdrlen; + + + k = init_buffers(&dsp); // baud difference not significant + if ( k < 0 ) { + fprintf(stderr, "error: init buffers\n"); return -1; } } - - symlen = 1; - - // 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_RATE6; - dsp.sps = (float)dsp.sr/dsp.br; - dsp.symlen = symlen; - dsp.symhd = 1; - dsp._spb = dsp.sps*symlen; - dsp.hdr = rawheader; - dsp.hdrlen = strlen(rawheader); - dsp.BT = 1.2; // bw/time (ISI) // 1.0..2.0 // BT(lmsX) < BT(lms6) ? -> init_buffers() - dsp.h = 0.9; // 0.95 modulation index - dsp.opt_iq = option_iq; - dsp.opt_lp = option_lp; - dsp.lpIQ_bw = 8e3; // IF lowpass bandwidth - dsp.lpFM_bw = 6e3; // 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); + else { + // init circular header bit buffer + hdb.hdr = rawheader; + hdb.len = strlen(rawheader); + //hdb.thb = 1.0 - 3.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen + hdb.bufpos = -1; + hdb.buf = NULL; + /* + calloc(hdb.len, sizeof(char)); + if (hdb.buf == NULL) { + fprintf(stderr, "error: malloc\n"); + return -1; + } + */ + hdb.ths = 0.7; // caution/test false positive + hdb.sbuf = calloc(hdb.len, sizeof(float)); + if (hdb.sbuf == NULL) { + fprintf(stderr, "error: malloc\n"); + return -1; + } } - //headerlen = dsp.hdrlen; - - k = init_buffers(&dsp); // baud difference not significant - if ( k < 0 ) { - fprintf(stderr, "error: init buffers\n"); - return -1; - }; - if (gpx->option.vit) { k = vit_initCodes(gpx); @@ -1194,9 +1275,13 @@ int main(int argc, char **argv) { while ( 1 ) { - // FM-audio: - header_found = find_header(&dsp, thres, 3, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0 - _mv = dsp.mv; + if (option_softin) { + header_found = find_softbinhead(fp, &hdb, &_mv); + } + else { // FM-audio: + header_found = find_header(&dsp, thres, 10, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0 + _mv = dsp.mv; + } if (header_found == EOF) break; @@ -1220,19 +1305,49 @@ int main(int argc, char **argv) { while ( pos < rawbitblock_len ) { - bitQ = read_slbit(&dsp, &rbit, 0, bitofs, bitpos, -1, 0); // symlen=1 - + if (option_softin) { + float s = 0.0; + bitQ = f32soft_read(fp, &s); + if (bitQ != EOF) { + rhsbit.sb = s; + rhsbit.hb = (s>=0.0); + } + } + else { + //bitQ = read_softbit(&dsp, &rhsbit, 0, bitofs, bitpos, -1, 0); // symlen=1 + bitQ = read_softbit2p(&dsp, &rhsbit, 0, bitofs, bitpos, -1, 0, &rhsbit1); // symlen=1 + if (gpx->option.ecc == 3) { + if (rhsbit.sb*rhsbit1.sb < 0) { + rhsbit.sb += rhsbit1.sb; + rhsbit.hb = (rhsbit.sb>=0.0); + } + } + } if (bitQ == EOF) { break; } - bit = rbit ^ (bc%2); // (c0,inv(c1)) - gpx->blk_rawbits[pos] = 0x30 + bit; + // optional: + // normalize soft bit s_j by + // rhsbit.sb /= dsp._spb+1; // all samples in [-1,+1] + // or at the end by max|s_j| over all bits in rawframe + // (only if |sj| >> 1 by factor 100) + + hsbit.hb = rhsbit.hb ^ (bc%2); // (c0,inv(c1)) + int sgn = -2*(((unsigned int)bc)%2)+1; + hsbit.sb = sgn * rhsbit.sb; + + if (gpx->option.vit == 1) { // hard decision + hsbit.sb = 2*hsbit.hb -1; + } + + gpx->blk_rawbits[pos] = hsbit; + gpx->blk_rawbits[pos].hb += 0x30; bc++; pos++; bitpos += 1; } - gpx->blk_rawbits[pos] = '\0'; + gpx->blk_rawbits[pos].hb = '\0'; time_elapsed_sec = dsp.sample_in / (double)dsp.sr; proc_frame(gpx, pos); @@ -1276,7 +1391,11 @@ int main(int argc, char **argv) { } - free_buffers(&dsp); + if (!option_softin) free_buffers(&dsp); + else { + if (hdb.buf) { free(hdb.buf); hdb.buf = NULL; } + } + if (gpx->vit) { free(gpx->vit); gpx->vit = NULL; } fclose(fp); diff --git a/demod/mod/lms6Xmod_soft.c b/demod/mod/lms6Xmod_soft.c deleted file mode 100644 index d02b39e..0000000 --- a/demod/mod/lms6Xmod_soft.c +++ /dev/null @@ -1,1328 +0,0 @@ - -/* - * LMS6 - * (403 MHz) - * - * soft decision test: - * IQ-decoding: --vit2 (soft decision) better performance (low dB) - * FM-decoding: --vit1 (hard decision) better than --vit2 - * - * sync header: correlation/matched filter - * files: lms6Xmod.c demod_mod.c demod_mod.h bch_ecc_mod.c bch_ecc_mod.h - * compile, either (a) or (b): - * (a) - * gcc -c demod_mod.c - * gcc -DINCLUDESTATIC lms6Xmod.c demod_mod.o -lm -o lms6Xmod - * (b) - * gcc -c demod_mod.c - * gcc -c bch_ecc_mod.c - * gcc lms6Xmod.c demod_mod.o bch_ecc_mod.o -lm -o lms6Xmod - * - * usage: - * ./lms6Xmod --vit --ecc - * ./lms6Xmod --vit2 --ecc --IQ 0.0 - * ( --vit/--vit2 recommended) - * author: zilog80 - */ - -#include -#include -#include -#include - -#ifdef CYGWIN - #include // cygwin: _setmode() - #include -#endif - - -//typedef unsigned char ui8_t; -//typedef unsigned short ui16_t; -//typedef unsigned int ui32_t; - -#include "demod_mod.h" - -//#define INCLUDESTATIC 1 -#ifdef INCLUDESTATIC - #include "bch_ecc_mod.c" -#else - #include "bch_ecc_mod.h" -#endif - - -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 vit; - i8_t jsn; // JSON output (auto_rx) -} option_t; - - -/* -------------------------------------------------------------------------- */ - -#define BAUD_RATE6 (4800.0) -#define BAUD_RATEX (4797.8) - -#define BITS 8 -#define HEADOFS 0 //16 -#define HEADLEN ((4*16)-HEADOFS) - -#define SYNC_LEN 5 -#define FRM_LEN (223) -#define PAR_LEN (32) -#define FRMBUF_LEN (3*FRM_LEN) -#define BLOCKSTART (SYNC_LEN*BITS*2) -#define BLOCK_LEN (FRM_LEN+PAR_LEN+SYNC_LEN) // 255+5 = 260 -#define RAWBITBLOCK_LEN (300*BITS*2) // (no tail) -#define RAWBITBLOCK_LEN_6 ((BLOCK_LEN+1)*BITS*2) // (+1 tail) LMS6 - -#define FRAME_LEN (300) // 4800baud, 16bits/byte -#define BITFRAME_LEN (FRAME_LEN*BITS) -#define RAWBITFRAME_LEN (BITFRAME_LEN*2) -#define OVERLAP 64 -#define OFS 4 - - -static char rawheader[] = "0101011000001000""0001110010010111""0001101010100111""0011110100111110"; // (c0,inv(c1)) -// (00) 58 f3 3f b8 -// char header[] = "0000001101011101""0100100111000010""0100111111110010""0110100001101011"; // (c0,c1) -static ui8_t rs_sync[] = { 0x00, 0x58, 0xf3, 0x3f, 0xb8}; -// 0x58f33fb8 little-endian <-> 0x1ACFFC1D big-endian bytes - -// (00) 58 f3 3f b8 -static char blk_syncbits[] = "0000000000000000""0000001101011101""0100100111000010""0100111111110010""0110100001101011"; - -static ui8_t frm_sync6[] = { 0x24, 0x54, 0x00, 0x00}; -static ui8_t frm_syncX[] = { 0x24, 0x46, 0x05, 0x00}; - - -#define L 7 // d_f=10 -static char polyA[] = "1001111"; // 0x4f: x^6+x^3+x^2+x+1 -static char polyB[] = "1101101"; // 0x6d: x^6+x^5+x^3+x^2+1 -/* -// d_f=6 -qA[] = "1110011"; // 0x73: x^6+x^5+x^4+x+1 -qB[] = "0011110"; // 0x1e: x^4+x^3+x^2+x -pA[] = "10010101"; // 0x95: x^7+x^4+x^2+1 = (x+1)(x^6+x^5+x^4+x+1) = (x+1)qA -pB[] = "00100010"; // 0x22: x^5+x = (x+1)(x^4+x^3+x^2+x)=x(x+1)^3 = (x+1)qB -polyA = qA + x*qB -polyB = qA + qB -*/ - -#define N (1 << L) -#define M (1 << (L-1)) - -typedef struct { - ui8_t bIn; - ui8_t codeIn; - ui8_t prevState; // 0..M=64 - float w; -} states_t; - -typedef struct { - hsbit_t rawbits[RAWBITFRAME_LEN+OVERLAP*BITS*2 +8]; - states_t state[RAWBITFRAME_LEN+OVERLAP +8][M]; - states_t d[N]; -} VIT_t; - -typedef struct { - int frnr; - int sn; - int week; - int gpstow; int gpssec; - double gpstowX; - 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 vE; double vN; double vU; - hsbit_t blk_rawbits[RAWBITBLOCK_LEN+SYNC_LEN*BITS*2 +9]; - ui8_t frame[FRM_LEN]; // = { 0x24, 0x54, 0x00, 0x00}; // dataheader - int frm_pos; // ecc_blk <-> frm_blk - int sf6; - int sfX; - int typ; - float frm_rate; - int auto_detect; - int reset_dsp; - option_t option; - RS_t RS; - VIT_t *vit; -} gpx_t; - - -/* ------------------------------------------------------------------------------------ */ -static int gpstow_start = -1; -static double time_elapsed_sec = 0.0; - -/* - * Convert GPS Week and Seconds to Modified Julian Day. - * - Adapted from sci.astro FAQ. - * - Ignores UTC leap seconds. - */ -// in : week, gpssec -// out: jahr, monat, tag -static void Gps2Date(gpx_t *gpx) { - long GpsDays, Mjd; - long _J, _C, _Y, _M; - - GpsDays = gpx->week * 7 + (gpx->gpssec / 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; - gpx->tag = _J - 2447 * _M / 80; - _J = _M / 11; - gpx->monat = _M + 2 - (12 * _J); - gpx->jahr = 100 * (_C - 49) + _Y + _J; -} -/* ------------------------------------------------------------------------------------ */ - -// ------------------------------------------------------------------------ - -static ui8_t vit_code[N]; -static int vitCodes_init = 0; - -static int vit_initCodes(gpx_t *gpx) { - int cA, cB; - int i, bits; - - VIT_t *pv = calloc(1, sizeof(VIT_t)); - if (pv == NULL) return -1; - gpx->vit = pv; - - if ( vitCodes_init == 0 ) { - for (bits = 0; bits < N; bits++) { - cA = 0; - cB = 0; - for (i = 0; i < L; i++) { - cA ^= (polyA[L-1-i]&1) & ((bits >> i)&1); - cB ^= (polyB[L-1-i]&1) & ((bits >> i)&1); - } - vit_code[bits] = (cA<<1) | cB; - } - vitCodes_init = 1; - } - - return 0; -} - -static float vit_dist2(int c, hsbit_t *rc) { - int c0 = 2*((c>>1) & 1)-1; // {0,1} -> {-1,+1} - int c1 = 2*(c & 1)-1; - float d2 = (c0-rc[0].sb)*(c0-rc[0].sb) + (c1-rc[1].sb)*(c1-rc[1].sb); - return d2; -} - -static int vit_start(VIT_t *vit, hsbit_t *rc) { - int t, m, j, c; - float d; - - t = L-1; - m = M; - while ( t > 0 ) { // t=0..L-2: nextStatestate[t][j].prevState = j/2; - } - t--; - m /= 2; - } - - m = 2; - for (t = 1; t < L; t++) { - for (j = 0; j < m; j++) { - c = vit_code[j]; - vit->state[t][j].bIn = j % 2; - vit->state[t][j].codeIn = c; - d = vit_dist2( c, rc+2*(t-1) ); - vit->state[t][j].w = vit->state[t-1][vit->state[t][j].prevState].w + d; - } - m *= 2; - } - - return t; -} - -static int vit_next(VIT_t *vit, int t, hsbit_t *rc) { - int b, nstate; - int j, index; - - for (j = 0; j < M; j++) { - for (b = 0; b < 2; b++) { - nstate = j*2 + b; - vit->d[nstate].bIn = b; - vit->d[nstate].codeIn = vit_code[nstate]; - vit->d[nstate].prevState = j; - vit->d[nstate].w = vit->state[t][j].w + vit_dist2( vit->d[nstate].codeIn, rc ); - } - } - - for (j = 0; j < M; j++) { - - if ( vit->d[j].w <= vit->d[j+M].w ) index = j; else index = j+M; - - vit->state[t+1][j] = vit->d[index]; - } - - return 0; -} - -static int vit_path(VIT_t *vit, int j, int t) { - int c; - - vit->rawbits[2*t].hb = '\0'; - while (t > 0) { - c = vit->state[t][j].codeIn; - vit->rawbits[2*t -2].hb = 0x30 + ((c>>1) & 1); - vit->rawbits[2*t -1].hb = 0x30 + (c & 1); - j = vit->state[t][j].prevState; - t--; - } - - return 0; -} - -static int hbstr_len(hsbit_t *hsbit) { - int len = 0; - while (hsbit[len].hb) len++; - return len; -} - -static int viterbi(VIT_t *vit, hsbit_t *rc) { - int t, tmax; - int j, j_min; - float w_min; - - vit_start(vit, rc); - - tmax = hbstr_len(rc)/2; - - for (t = L-1; t < tmax; t++) - { - vit_next(vit, t, rc+2*t); - } - - w_min = -1; - for (j = 0; j < M; j++) { - if (w_min < 0) { - w_min = vit->state[tmax][j].w; - j_min = j; - } - if (vit->state[tmax][j].w < w_min) { - w_min = vit->state[tmax][j].w; - j_min = j; - } - } - vit_path(vit, j_min, tmax); - - return 0; -} - -// ------------------------------------------------------------------------ - -static int deconv(hsbit_t *rawbits, char *bits) { - - int j, n, bitA, bitB; - hsbit_t *p; - int len; - int errors = 0; - int m = L-1; - - len = hbstr_len(rawbits); - for (j = 0; j < m; j++) bits[j] = '0'; - n = 0; - while ( 2*(m+n) < len ) { - p = rawbits+2*(m+n); - bitA = bitB = 0; - for (j = 0; j < m; j++) { - bitA ^= (bits[n+j]&1) & (polyA[j]&1); - bitB ^= (bits[n+j]&1) & (polyB[j]&1); - } - if ( (bitA^(p[0].hb&1))==(polyA[m]&1) && (bitB^(p[1].hb&1))==(polyB[m]&1) ) bits[n+m] = '1'; - else if ( (bitA^(p[0].hb&1))==0 && (bitB^(p[1].hb&1))==0 ) bits[n+m] = '0'; - else { - if ( (bitA^(p[0].hb&1))!=(polyA[m]&1) && (bitB^(p[1].hb&1))==(polyB[m]&1) ) bits[n+m] = 0x39; - else bits[n+m] = 0x38; - errors = n; - break; - } - n += 1; - } - bits[n+m] = '\0'; - - return errors; -} - -// ------------------------------------------------------------------------ - -static int crc16_0(ui8_t frame[], int len) { - int crc16poly = 0x1021; - int rem = 0x0, i, j; - int byte; - - for (i = 0; i < len; i++) { - byte = frame[i]; - rem = rem ^ (byte << 8); - for (j = 0; j < 8; j++) { - if (rem & 0x8000) { - rem = (rem << 1) ^ crc16poly; - } - else { - rem = (rem << 1); - } - rem &= 0xFFFF; - } - } - return rem; -} - -static int check_CRC(ui8_t frame[]) { - ui32_t crclen = 0, - crcdat = 0; - - crclen = 221; - crcdat = (frame[crclen]<<8) | frame[crclen+1]; - if ( crcdat != crc16_0(frame, crclen) ) { - return 1; // CRC NO - } - else return 0; // CRC OK -} - -// ------------------------------------------------------------------------ - -static int bits2bytes(char *bitstr, ui8_t *bytes) { - int i, bit, d, byteval; - int len = strlen(bitstr)/8; - int bitpos, bytepos; - - bitpos = 0; - bytepos = 0; - - while (bytepos < len) { - - byteval = 0; - d = 1; - for (i = 0; i < BITS; i++) { - bit=*(bitstr+bitpos+i); /* little endian */ - //bit=*(bitstr+bitpos+7-i); /* big endian */ - if ((bit == '1') || (bit == '9')) byteval += d; - else /*if ((bit == '0') || (bit == '8'))*/ byteval += 0; - d <<= 1; - } - bitpos += BITS; - bytes[bytepos++] = byteval & 0xFF; - } - - //while (bytepos < FRAME_LEN+OVERLAP) bytes[bytepos++] = 0; - - return bytepos; -} - -/* -------------------------------------------------------------------------- */ - - -#define pos_SondeSN (OFS+0x00) // ?4 byte 00 7A.... -#define pos_FrameNb (OFS+0x04) // 2 byte -//GPS -#define pos_GPSTOW (OFS+0x06) // 4/8 byte -#define pos_GPSlat (OFS+0x0E) // 4 byte -#define pos_GPSlon (OFS+0x12) // 4 byte -#define pos_GPSalt (OFS+0x16) // 4 byte -//GPS Velocity (ENU) LMS-6 -#define pos_GPSvE (OFS+0x1A) // 3 byte -#define pos_GPSvN (OFS+0x1D) // 3 byte -#define pos_GPSvU (OFS+0x20) // 3 byte -//GPS Velocity (HDV) LMS-X -#define pos_GPSvH (OFS+0x1A) // 2 byte -#define pos_GPSvD (OFS+0x1C) // 2 byte -#define pos_GPSvV (OFS+0x1E) // 2 byte - - -static int get_SondeSN(gpx_t *gpx) { - unsigned byte; - - byte = (gpx->frame[pos_SondeSN]<<24) | (gpx->frame[pos_SondeSN+1]<<16) - | (gpx->frame[pos_SondeSN+2]<<8) | gpx->frame[pos_SondeSN+3]; - gpx->sn = byte & 0xFFFFFF; - - return 0; -} - -static int get_FrameNb(gpx_t *gpx) { - ui8_t *frnr_bytes; - int frnr; - - frnr_bytes = gpx->frame+pos_FrameNb; - - frnr = (frnr_bytes[0] << 8) + frnr_bytes[1] ; - gpx->frnr = frnr; - - 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 crc_err) { - int i; - ui8_t *gpstime_bytes; - int gpstime = 0, // 32bit - day; - float ms; - - gpstime_bytes = gpx->frame+pos_GPSTOW; - - gpstime = 0; - for (i = 0; i < 4; i++) { - gpstime |= gpstime_bytes[i] << (8*(3-i)); - } - - if (gpstow_start < 0 && !crc_err) { - gpstow_start = gpstime; // time elapsed since start-up? - if (gpx->week > 0 && gpstime/1000.0 < time_elapsed_sec) gpx->week += 1; - } - gpx->gpstow = gpstime; // tow/ms - - ms = gpstime % 1000; - gpstime /= 1000; - gpx->gpssec = gpstime; - - day = gpstime / (24 * 3600); - gpstime %= (24*3600); - - if ((day < 0) || (day > 6)) return -1; - - gpx->wday = day; - gpx->std = gpstime / 3600; - gpx->min = (gpstime % 3600) / 60; - gpx->sek = gpstime % 60 + ms/1000.0; - - return 0; -} - -static int get_GPStime_X(gpx_t *gpx) { - int i; - unsigned byte; - ui32_t gpstime, tow_u4; - int day; - float ms; - ui32_t w[2]; // 64bit float - double *f64 = (double*)w; - - w[0] = 0; - for (i = 0; i < 4; i++) { - byte = gpx->frame[pos_GPSTOW + i]; - w[0] |= byte << (8*(3-i)); - } - w[1] = 0; - for (i = 0; i < 4; i++) { - byte = gpx->frame[pos_GPSTOW+4 + i]; - w[1] |= byte << (8*(3-i)); - } - - gpx->gpstowX = *f64; - gpx->gpstow = (ui32_t)(gpx->gpstowX*1e3); // tow/ms - tow_u4 = (ui32_t)gpx->gpstowX; - gpstime = tow_u4; - gpx->gpssec = tow_u4; - - day = gpstime / (24 * 3600); - gpstime %= (24*3600); - - if ((day < 0) || (day > 6)) return -1; - - gpx->wday = day; - gpx->std = gpstime / 3600; - gpx->min = (gpstime % 3600) / 60; - gpx->sek = (gpstime % 60) + *f64 - tow_u4; - - return 0; -} - -static double B60B60 = (1<<30)/90.0; // 2^32/360 = 2^30/90 = 0xB60B60.711x - -static int get_GPSlat(gpx_t *gpx) { - int i; - ui8_t *gpslat_bytes; - int gpslat; - double lat; - - gpslat_bytes = gpx->frame+pos_GPSlat; - - gpslat = 0; - for (i = 0; i < 4; i++) { - gpslat |= gpslat_bytes[i] << (8*(3-i)); - } - if (gpx->typ == 6) lat = gpslat / B60B60; - else /*typ==10*/ lat = gpslat / 1e7; - - gpx->lat = lat; - - return 0; -} - -static int get_GPSlon(gpx_t *gpx) { - int i; - ui8_t *gpslon_bytes; - int gpslon; - double lon; - - gpslon_bytes = gpx->frame+pos_GPSlon; - - gpslon = 0; - for (i = 0; i < 4; i++) { - gpslon |= gpslon_bytes[i] << (8*(3-i)); - } - - if (gpx->typ == 6) lon = gpslon / B60B60; - else /*typ==10*/ lon = gpslon / 1e7; - - gpx->lon = lon; - - return 0; -} - -static int get_GPSalt(gpx_t *gpx) { - int i; - ui8_t *gpsheight_bytes; - int gpsheight; - double alt; - - gpsheight_bytes = gpx->frame+pos_GPSalt; - - gpsheight = 0; - for (i = 0; i < 4; i++) { - gpsheight |= gpsheight_bytes[i] << (8*(3-i)); - } - - if (gpx->typ == 6) alt = gpsheight / 1000.0; - else /*typ==10*/ alt = gpsheight / 100.0; - - gpx->alt = alt; - - if (alt < -200 || alt > 60000) return -1; - return 0; -} - -// LMS-6 -static int get_GPSvel24(gpx_t *gpx) { - ui8_t *gpsVel_bytes; - int vel24; - double vx, vy, vz, dir; - - gpsVel_bytes = gpx->frame+pos_GPSvE; - vel24 = gpsVel_bytes[0] << 16 | gpsVel_bytes[1] << 8 | gpsVel_bytes[2]; - if (vel24 > (0x7FFFFF)) vel24 -= 0x1000000; - vx = vel24 / 1e3; // east - - gpsVel_bytes = gpx->frame+pos_GPSvN; - vel24 = gpsVel_bytes[0] << 16 | gpsVel_bytes[1] << 8 | gpsVel_bytes[2]; - if (vel24 > (0x7FFFFF)) vel24 -= 0x1000000; - vy = vel24 / 1e3; // north - - gpsVel_bytes = gpx->frame+pos_GPSvU; - vel24 = gpsVel_bytes[0] << 16 | gpsVel_bytes[1] << 8 | gpsVel_bytes[2]; - if (vel24 > (0x7FFFFF)) vel24 -= 0x1000000; - vz = vel24 / 1e3; // up - - gpx->vE = vx; - gpx->vN = vy; - gpx->vU = vz; - - - 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; - - gpx->vV = vz; - - return 0; -} - -// LMS-X -static int get_GPSvel16_X(gpx_t *gpx) { - ui8_t *gpsVel_bytes; - short vel16; - double vx, vy, vz; - - gpsVel_bytes = gpx->frame+pos_GPSvH; - vel16 = gpsVel_bytes[0] << 8 | gpsVel_bytes[1]; - vx = vel16 / 1e2; // horizontal - - gpsVel_bytes = gpx->frame+pos_GPSvD; - vel16 = gpsVel_bytes[0] << 8 | gpsVel_bytes[1]; - vy = vel16 / 1e2; // direction/course - - gpsVel_bytes = gpx->frame+pos_GPSvV; - vel16 = gpsVel_bytes[0] << 8 | gpsVel_bytes[1]; - vz = vel16 / 1e2; // vertical - - gpx->vH = vx; - gpx->vD = vy; - gpx->vV = vz; - - return 0; -} - - -// RS(255,223)-CCSDS -#define rs_N 255 -#define rs_K 223 -#define rs_R (rs_N-rs_K) // 32 - -static int lms6_ecc(gpx_t *gpx, ui8_t *cw) { - int errors; - ui8_t err_pos[rs_R], - err_val[rs_R]; - - errors = rs_decode(&gpx->RS, cw, err_pos, err_val); - - return errors; -} - -static void print_frame(gpx_t *gpx, int crc_err, int len) { - int err1=0, err2=0; - - if (gpx->frame[0] != 0) - { - //if ((gpx->frame[pos_SondeSN+1] & 0xF0) == 0x70) // ? beginnen alle SNs mit 0x7A.... bzw 80..... ? - if ( gpx->frame[pos_SondeSN+1] ) - { - get_SondeSN(gpx); - get_FrameNb(gpx); - printf(" (%7d) ", gpx->sn); - printf(" [%5d] ", gpx->frnr); - - get_GPSlat(gpx); - get_GPSlon(gpx); - err2 = get_GPSalt(gpx); - if (gpx->typ == 6) - { - err1 = get_GPStime(gpx, crc_err); - get_GPSvel24(gpx); - } - else { - err1 = get_GPStime_X(gpx); //, crc_err - get_GPSvel16_X(gpx); - } - - if (!err1) printf("%s ", weekday[gpx->wday]); - if (gpx->week > 0) { - if (gpx->gpstow < gpstow_start && !crc_err) { - gpx->week += 1; // week roll-over - gpstow_start = gpx->gpstow; - } - Gps2Date(gpx); - fprintf(stdout, "%04d-%02d-%02d ", gpx->jahr, gpx->monat, gpx->tag); - } - printf("%02d:%02d:%06.3f ", gpx->std, gpx->min, gpx->sek); // falls Rundung auf 60s: Ueberlauf - - if (!err2) { - printf(" lat: %.5f ", gpx->lat); - printf(" lon: %.5f ", gpx->lon); - printf(" alt: %.2fm ", gpx->alt); - printf(" vH: %.1fm/s D: %.1f vV: %.1fm/s ", gpx->vH, gpx->vD, gpx->vV); - } - - if (crc_err==0) printf(" [OK]"); else printf(" [NO]"); - - printf("\n"); - - - if (gpx->option.jsn) { - // Print JSON output required by auto_rx. - if (crc_err==0) { // CRC-OK - // UTC oder GPS? - char sntyp[] = "LMS6-"; - if (gpx->typ == 10) sntyp[3] = 'X'; - 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"); - } - } - - } - } -} - -static int frmsync_6(gpx_t *gpx, ui8_t block_bytes[], int blk_pos) { - int j; - - while ( blk_pos-SYNC_LEN < FRM_LEN ) { - gpx->sf6 = 0; - for (j = 0; j < 4; j++) gpx->sf6 += (block_bytes[blk_pos+j] == frm_sync6[j]); - if (gpx->sf6 == 4) { - gpx->frm_pos = 0; - break; - } - blk_pos++; - } - - return blk_pos; -} - -static int frmsync_X(gpx_t *gpx, ui8_t block_bytes[]) { - int j; - int blk_pos = SYNC_LEN; - - gpx->sfX = 0; - for (j = 0; j < 4; j++) gpx->sfX += (block_bytes[SYNC_LEN+j] == frm_syncX[j]); - if (gpx->sfX < 4) { // scan 1..40 ? - gpx->sfX = 0; - for (j = 0; j < 4; j++) gpx->sfX += (block_bytes[SYNC_LEN+35+j] == frm_syncX[j]); - if (gpx->sfX == 4) blk_pos = SYNC_LEN+35; - else { - gpx->sfX = 0; - for (j = 0; j < 4; j++) gpx->sfX += (block_bytes[SYNC_LEN+40+j] == frm_syncX[j]); - if (gpx->sfX == 4) blk_pos = SYNC_LEN+40; // 300-260 - } - } - - return blk_pos; -} - -static void proc_frame(gpx_t *gpx, int len) { - int blk_pos = SYNC_LEN; - ui8_t block_bytes[FRAME_LEN+8]; - ui8_t rs_cw[rs_N]; - char frame_bits[BITFRAME_LEN+OVERLAP*BITS +8]; // init L-1 bits mit 0 - hsbit_t *rawbits = NULL; - int i, j; - int err = 0; - int errs = 0; - int crc_err = 0; - int flen, blen; - - - if ((len % 8) > 4) { - while (len % 8) { - gpx->blk_rawbits[len].hb = '0'; - gpx->blk_rawbits[len].sb = -1; - len++; - } - } - gpx->blk_rawbits[len].hb = '\0'; - - flen = len / (2*BITS); - - if (gpx->option.vit) { - viterbi(gpx->vit, gpx->blk_rawbits); - rawbits = gpx->vit->rawbits; - } - else rawbits = gpx->blk_rawbits; - - err = deconv(rawbits, frame_bits); - - if (err) { for (i=err; i < RAWBITBLOCK_LEN/2; i++) frame_bits[i] = 0; } - - - blen = bits2bytes(frame_bits, block_bytes); - for (j = blen; j < FRAME_LEN+8; j++) block_bytes[j] = 0; - - - blk_pos = SYNC_LEN; - - if (gpx->typ == 6) - { - if (gpx->option.ecc) { - for (j = 0; j < rs_N; j++) rs_cw[rs_N-1-j] = block_bytes[SYNC_LEN+j]; - errs = lms6_ecc(gpx, rs_cw); - for (j = 0; j < rs_N; j++) block_bytes[SYNC_LEN+j] = rs_cw[rs_N-1-j]; - } - - while ( blk_pos-SYNC_LEN < FRM_LEN ) { - - if (gpx->sf6 == 0) - { - blk_pos = frmsync_6(gpx, block_bytes, blk_pos); - - if (gpx->sf6 < 4) { - frmsync_X(gpx, block_bytes); // pos(frm_syncX[]) < 46: different baud not significant - if (gpx->sfX == 4) { - if (gpx->auto_detect) { gpx->typ = 10; gpx->reset_dsp = 1; } - break; - } - } - } - - if ( gpx->sf6 && gpx->frm_pos < FRM_LEN ) { - gpx->frame[gpx->frm_pos] = block_bytes[blk_pos]; - gpx->frm_pos++; - blk_pos++; - } - - if (gpx->frm_pos == FRM_LEN) { - - crc_err = check_CRC(gpx->frame); - - if (gpx->option.raw == 1) { - for (i = 0; i < FRM_LEN; i++) printf("%02x ", gpx->frame[i]); - if (crc_err==0) printf(" [OK]"); else printf(" [NO]"); - printf("\n"); - } - - if (gpx->option.raw == 0) print_frame(gpx, crc_err, len); - - gpx->frm_pos = 0; - gpx->sf6 = 0; - } - } - } - - if (gpx->typ == 10) - { - blk_pos = frmsync_X(gpx, block_bytes); - - if (gpx->sfX < 4) { - //blk_pos = SYNC_LEN; - while ( blk_pos-SYNC_LEN < FRM_LEN ) { - gpx->sf6 = 0; - for (j = 0; j < 4; j++) gpx->sf6 += (block_bytes[blk_pos+j] == frm_sync6[j]); - if (gpx->sf6 == 4) { - gpx->frm_pos = 0; - if (gpx->auto_detect) { gpx->typ = 6; gpx->reset_dsp = 1; } - break; - } - blk_pos++; - } - - // check frame timing vs baud - // LMS6: frm_rate = 4800.0 * FRAME_LEN/BLOCK_LEN = 4800*300/260 = 5538 - // LMSX: delta_mp = 4797.8 (longer timesync-frames possible) - if (gpx->frm_rate > 5000.0 || gpx->frm_rate < 4000.0) { // lms6-blocklen = 260/300 sr, sync wird ueberlesen ... - if (gpx->auto_detect) { gpx->typ = 6; gpx->reset_dsp = 1; } - } - } - else - { - if (blen > 100 && gpx->option.ecc) { - for (j = 0; j < rs_N; j++) rs_cw[rs_N-1-j] = block_bytes[blk_pos+j]; - errs = lms6_ecc(gpx, rs_cw); - for (j = 0; j < rs_N; j++) block_bytes[blk_pos+j] = rs_cw[rs_N-1-j]; - } - - for (j = 0; j < rs_K; j++) gpx->frame[j] = block_bytes[blk_pos+j]; - - crc_err = check_CRC(gpx->frame); - - if (gpx->option.raw == 1) { - for (i = 0; i < FRM_LEN; i++) printf("%02x ", gpx->frame[i]); - if (crc_err==0) printf(" [OK]"); else printf(" [NO]"); - printf("\n"); - } - - if (gpx->option.raw == 0) print_frame(gpx, crc_err, len); - } - } - -} - - -int main(int argc, char **argv) { - - int option_inv = 0; // invertiert Signal - 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 gpsweek = 0; - - FILE *fp = NULL; - char *fpname = NULL; - - int k; - - hsbit_t hsbit, rhsbit; - int bitpos = 0; - int bitQ; - int pos; - //int headerlen = 0; - - int header_found = 0; - - float thres = 0.65; - float _mv = 0.0; - - int symlen = 1; - int bitofs = 0; - int shift = 0; - - int bitofs6 = 1; // +1 .. +2 - int bitofsX = 0; // 0 .. +1 - - unsigned int bc = 0; - - ui32_t rawbitblock_len = RAWBITBLOCK_LEN_6; - ui32_t mpos0 = 0; - - - pcm_t pcm = {0}; - dsp_t dsp = {0}; //memset(&dsp, 0, sizeof(dsp)); -/* - // gpx_t _gpx = {0}; gpx_t *gpx = &_gpx; // stack size ... - gpx_t *gpx = NULL; - gpx = calloc(1, sizeof(gpx_t)); - //memset(gpx, 0, sizeof(gpx_t)); -*/ - gpx_t _gpx = {0}; gpx_t *gpx = &_gpx; - - gpx->auto_detect = 1; - gpx->reset_dsp = 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, " --vit (Viterbi)\n"); - fprintf(stderr, " --ecc (Reed-Solomon)\n"); - return 0; - } - else if (strcmp(*argv, "--lms6" ) == 0) { - gpx->typ = 6; - gpx->auto_detect = 0; - } - else if (strcmp(*argv, "--lmsX" ) == 0) { - gpx->typ = 10; - gpx->auto_detect = 0; - } - else if ( (strcmp(*argv, "-v") == 0) || (strcmp(*argv, "--verbose") == 0) ) { - gpx->option.vbs = 1; - } - else if ( (strcmp(*argv, "-r") == 0) || (strcmp(*argv, "--raw") == 0) ) { - gpx->option.raw = 1; // bytes - rs_ecc_codewords - } - else if (strcmp(*argv, "--ecc" ) == 0) { gpx->option.ecc = 1; } // RS-ECC - else if (strcmp(*argv, "--vit" ) == 0) { gpx->option.vit = 1; } // viterbi-hard - else if (strcmp(*argv, "--vit2" ) == 0) { gpx->option.vit = 2; } // viterbi-soft - else if ( (strcmp(*argv, "--gpsweek") == 0) ) { - ++argv; - if (*argv) { - gpsweek = atoi(*argv); - if (gpsweek < 1024 || gpsweek > 3072) gpsweek = 0; - } - else return -1; - } - else if ( (strcmp(*argv, "-i") == 0) || (strcmp(*argv, "--invert") == 0) ) { - option_inv = 1; // nicht noetig - } - 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 , -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; - gpx->option.ecc = 1; - gpx->option.vit = 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, "- \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; - - - if (gpx->option.raw == 4) gpx->option.ecc = 1; - - // init gpx - memcpy(gpx->frame, frm_sync6, sizeof(frm_sync6)); - gpx->frm_pos = 0; // ecc_blk <-> frm_blk - gpx->sf6 = 0; - gpx->sfX = 0; - //memcpy(gpx->blk_rawbits, blk_syncbits, sizeof(blk_syncbits)); - for (k = 0; k < strlen(blk_syncbits); k++) { // strlen(blk_syncbits)=BLOCKSTART - int hbit = blk_syncbits[k] & 1; - gpx->blk_rawbits[k].hb = hbit + 0x30; - gpx->blk_rawbits[k].sb = 2*hbit-1; - } - - - gpx->option.inv = option_inv; // irrelevant - - gpx->week = gpsweek; - - if (option_iq == 0 && option_pcmraw) { - fclose(fp); - fprintf(stderr, "error: raw data not IQ\n"); - return -1; - } - if (option_iq == 0 && gpx->option.vit == 2) { // FM-demodulated data not recommended - gpx->option.vit = 1; // for soft-decoding - fprintf(stderr, "info: soft decoding only for IQ\n"); - } - 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; - } - } - - symlen = 1; - - // 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_RATE6; - dsp.sps = (float)dsp.sr/dsp.br; - dsp.symlen = symlen; - dsp.symhd = 1; - dsp._spb = dsp.sps*symlen; - dsp.hdr = rawheader; - dsp.hdrlen = strlen(rawheader); - dsp.BT = 1.2; // bw/time (ISI) // 1.0..2.0 // BT(lmsX) < BT(lms6) ? -> init_buffers() - dsp.h = 0.9; // 0.95 modulation index - dsp.opt_iq = option_iq; - dsp.opt_lp = option_lp; - dsp.lpIQ_bw = 8e3; // IF lowpass bandwidth - dsp.lpFM_bw = 6e3; // 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); - } - - //headerlen = dsp.hdrlen; - - k = init_buffers(&dsp); // baud difference not significant - if ( k < 0 ) { - fprintf(stderr, "error: init buffers\n"); - return -1; - }; - - - if (gpx->option.vit) { - k = vit_initCodes(gpx); - if (k < 0) return -1; - } - if (gpx->option.ecc) { - rs_init_RS255ccsds(&gpx->RS); // bch_ecc.c - } - - - // auto_detect: init LMS6 - bitofs = bitofs6 + shift; - rawbitblock_len = RAWBITBLOCK_LEN_6; - if (gpx->auto_detect) gpx->typ = 6; - - if (gpx->auto_detect == 0) { - if (gpx->typ == 6) { - // set lms6 - rawbitblock_len = RAWBITBLOCK_LEN_6; - dsp.br = (float)BAUD_RATE6; - dsp.sps = (float)dsp.sr/dsp.br; - bitofs = bitofs6 + shift; - } - if (gpx->typ == 10) { - // set lmsX - rawbitblock_len = RAWBITBLOCK_LEN;//_X; - dsp.br = (float)BAUD_RATEX; - dsp.sps = (float)dsp.sr/dsp.br; - bitofs = bitofsX + shift; - } - } - - - while ( 1 ) - { - // FM-audio: - header_found = find_header(&dsp, thres, 10, 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; // LMS-403: irrelevant - } - - if (header_found) { - - // LMS6: delta_mp = sr * BLOCK_LEN/FRAME_LEN = sr*260/300 - // LMSX: delta_mp = sr * 4800/4797.7 (or sync-reset) - gpx->frm_rate = 4800.0 * dsp.sr/(double)(dsp.mv_pos - mpos0); - mpos0 = dsp.mv_pos; - - - bitpos = 0; - pos = BLOCKSTART; - - if (_mv > 0) bc = 0; else bc = 1; - - while ( pos < rawbitblock_len ) { - - bitQ = read_softbit(&dsp, &rhsbit, 0, bitofs, bitpos, -1, 0); // symlen=1 - if (bitQ == EOF) { break; } - - // optional: - // normalize soft bit s_j by - // rhsbit.sb /= dsp._spb+1; // all samples in [-1,+1] - // or at the end by max|s_j| over all bits in rawframe - // (only if |sj| >> 1 by factor 100) - - hsbit.hb = rhsbit.hb ^ (bc%2); // (c0,inv(c1)) - int sgn = -2*(((unsigned int)bc)%2)+1; - hsbit.sb = sgn * rhsbit.sb; - - if (gpx->option.vit == 1) { // hard decision - hsbit.sb = 2*hsbit.hb -1; - } - - gpx->blk_rawbits[pos] = hsbit; - gpx->blk_rawbits[pos].hb += 0x30; - - bc++; - pos++; - bitpos += 1; - } - - gpx->blk_rawbits[pos].hb = '\0'; - - time_elapsed_sec = dsp.sample_in / (double)dsp.sr; - proc_frame(gpx, pos); - - if (pos < rawbitblock_len) break; - - pos = BLOCKSTART; - header_found = 0; - - if ( gpx->auto_detect && gpx->reset_dsp ) { - if (gpx->typ == 10) { - // set lmsX - rawbitblock_len = RAWBITBLOCK_LEN;//_X; - dsp.br = (float)BAUD_RATEX; - dsp.sps = (float)dsp.sr/dsp.br; - - // reset F1sum, F2sum - for (k = 0; k < dsp.N_IQBUF; k++) dsp.rot_iqbuf[k] = 0; - dsp.F1sum = 0; - dsp.F2sum = 0; - - bitofs = bitofsX + shift; - } - if (gpx->typ == 6) { - // set lms6 - rawbitblock_len = RAWBITBLOCK_LEN_6; - dsp.br = (float)BAUD_RATE6; - dsp.sps = (float)dsp.sr/dsp.br; - - // reset F1sum, F2sum - for (k = 0; k < dsp.N_IQBUF; k++) dsp.rot_iqbuf[k] = 0; - dsp.F1sum = 0; - dsp.F2sum = 0; - - bitofs = bitofs6 + shift; - } - gpx->reset_dsp = 0; - } - - } - } - - - free_buffers(&dsp); - if (gpx->vit) { free(gpx->vit); gpx->vit = NULL; } - - fclose(fp); - - return 0; -} - diff --git a/demod/mod/m10mod.c b/demod/mod/m10mod.c index 1a37f37..4222d4d 100644 --- a/demod/mod/m10mod.c +++ b/demod/mod/m10mod.c @@ -1102,6 +1102,8 @@ static int print_frame(gpx_t *gpx, int pos) { return (gpx->frame_bytes[0]<<8)|gpx->frame_bytes[1]; } +/* -------------------------------------------------------------------------- */ + int main(int argc, char **argv) { @@ -1115,6 +1117,8 @@ int main(int argc, char **argv) { int option_iq = 0; int option_lp = 0; int option_dc = 0; + int option_chk = 0; + int option_softin = 0; int option_pcmraw = 0; int wavloaded = 0; int sel_wavch = 0; // audio channel: left @@ -1129,6 +1133,7 @@ int main(int argc, char **argv) { int bitpos = 0; int bitQ; int pos; + hsbit_t hsbit, hsbit1; //int headerlen = 0; @@ -1144,6 +1149,8 @@ int main(int argc, char **argv) { pcm_t pcm = {0}; dsp_t dsp = {0}; //memset(&dsp, 0, sizeof(dsp)); + hdb_t hdb = {0}; + gpx_t gpx = {0}; @@ -1185,8 +1192,10 @@ int main(int argc, char **argv) { 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) ) { + else if (strcmp(*argv, "--chk3") == 0) { option_chk = 3; } + else if (strcmp(*argv, "--ch2") == 0) { sel_wavch = 1; } // right channel (default: 0=left) + else if (strcmp(*argv, "--softin") == 0) { option_softin = 1; } // float32 soft input + else if (strcmp(*argv, "--ths") == 0) { ++argv; if (*argv) { thres = atof(*argv); @@ -1250,6 +1259,7 @@ int main(int argc, char **argv) { if (!wavloaded) fp = stdin; + // init gpx gpx.option.inv = option_inv; // irrelevant gpx.option.vbs = option_verbose; gpx.option.raw = option_raw; @@ -1257,71 +1267,105 @@ int main(int argc, char **argv) { 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; + #ifdef EXT_FSK + if (!option_softin) { + option_softin = 1; + fprintf(stderr, "reading float32 soft symbols\n"); } - if (option_iq) sel_wavch = 0; + #endif - pcm.sel_ch = sel_wavch; - if (option_pcmraw == 0) { - k = read_wav_header(&pcm, fp); - if ( k < 0 ) { + if (!option_softin) { + + if (option_iq == 0 && option_pcmraw) { fclose(fp); - fprintf(stderr, "error: wav header\n"); + 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 + dsp.h = 0.9; // 1.2 modulation index + 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); + } + + //headerlen = dsp.hdrlen; + + + k = init_buffers(&dsp); + if ( k < 0 ) { + fprintf(stderr, "error: init buffers\n"); + return -1; + } + + bitofs += shift; + } + else { + // init circular header bit buffer + hdb.hdr = rawheader; + hdb.len = strlen(rawheader); + //hdb.thb = 1.0 - 3.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen + hdb.bufpos = -1; + hdb.buf = NULL; + /* + calloc(hdb.len, sizeof(char)); + if (hdb.buf == NULL) { + fprintf(stderr, "error: malloc\n"); + return -1; + } + */ + hdb.ths = 0.8; // caution 0.7: false positive / offset + hdb.sbuf = calloc(hdb.len, sizeof(float)); + if (hdb.sbuf == NULL) { + fprintf(stderr, "error: malloc\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 - dsp.h = 0.9; // 1.2 modulation index - 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); - } - - //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 (option_softin) { + header_found = find_softbinhead(fp, &hdb, &_mv); + } + 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; @@ -1339,15 +1383,31 @@ int main(int argc, char **argv) { 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); + if (option_softin) { + float s1 = 0.0; + float s2 = 0.0; + float s = 0.0; + bitQ = f32soft_read(fp, &s1); + if (bitQ != EOF) { + bitQ = f32soft_read(fp, &s2); + if (bitQ != EOF) { + s = s2-s1; // integrate both symbols // only 2nd Manchester symbol: s2 + bit = (s>=0.0); // no soft decoding + } + } } else { - bitQ = read_slbit(&dsp, &bit, 0/*gpx.option.inv*/, bitofs, bitpos, -1, spike); // symlen=2 + float bl = -1; + if (option_iq >= 2) spike = 0; + if (option_iq > 2) bl = 4.0; + //bitQ = read_slbit(&dsp, &bit, 0, bitofs, bitpos, bl, spike); // symlen=2 + bitQ = read_softbit2p(&dsp, &hsbit, 0, bitofs, bitpos, bl, spike, &hsbit1); // symlen=1 + bit = hsbit.hb; + if (option_chk == 3 && option_iq) { + //if (hsbit.sb*hsbit1.sb < 0) + bit = (hsbit.sb+0.25*hsbit1.sb)>=0; + } } - if ( bitQ == EOF ) { break; } gpx.frame_bits[pos] = 0x31 ^ (bit0 ^ bit); @@ -1364,7 +1424,13 @@ int main(int argc, char **argv) { // 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 (option_softin) { + float s = 0.0; + bitQ = f32soft_read(fp, &s); + } + else { + bitQ = read_slbit(&dsp, &bit, 0, bitofs, bitpos, -1, 0); // symlen=2 + } if ( bitQ == EOF) break; bitpos++; } @@ -1374,7 +1440,11 @@ int main(int argc, char **argv) { } } - free_buffers(&dsp); + + if (!option_softin) free_buffers(&dsp); + else { + if (hdb.buf) { free(hdb.buf); hdb.buf = NULL; } + } fclose(fp); diff --git a/demod/mod/mXXmod.c b/demod/mod/mXXmod.c index fa82fa5..5cca44e 100644 --- a/demod/mod/mXXmod.c +++ b/demod/mod/mXXmod.c @@ -192,7 +192,8 @@ frame[0x44..0x45]: frame check #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_SN 0x12 // 3 byte +#define pos_CNT 0x15 // 1 byte #define pos_BlkChk 0x16 // 2 byte #define pos_Check (stdFLEN-1) // 2 byte @@ -209,6 +210,7 @@ frame[0x44..0x45]: frame check #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" // 3 byte #define col_GPSdate "\x1b[38;5;94m" //111 @@ -217,6 +219,7 @@ frame[0x44..0x45]: frame check #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_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" @@ -530,7 +533,7 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { if (gpx->option.col) { fprintf(stdout, col_TXT); if (gpx->option.vbs >= 3) { - fprintf(stdout, "[%3d]", gpx->frame_bytes[pos_Cnt]); + 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]); @@ -543,14 +546,14 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { 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 || csOK)) { // SN - ui8_t b0 = gpx->frame_bytes[0x12]; - ui32_t s2 = (gpx->frame_bytes[0x14]<<8) | gpx->frame_bytes[0x13]; + ui8_t b0 = gpx->frame_bytes[pos_SN]; //0x12 + ui32_t s2 = (gpx->frame_bytes[pos_SN+2]<<8) | gpx->frame_bytes[pos_SN+1]; ui8_t ym = b0 & 0x7F; // #{0x0,..,0x77}=120=10*12 ui8_t y = ym / 12; ui8_t m = (ym % 12)+1; // there is b0=0x69<0x80 from 2018-09-19 ... fprintf(stdout, " (%u%02u", y, m); // more samples needed fprintf(stdout, " _ "); // (b0>>7)+1? (s2&0x3)+2? - fprintf(stdout, "_"); // ? (s2>>13)&0x3 ?? (s2&0x3)? + fprintf(stdout, "_"); // ?(s2>>(2+13))&0x1 ?? (s2&0x3)? fprintf(stdout, "%04u)", (s2>>2)&0x1FFF); } if (gpx->option.vbs >= 2) { @@ -570,7 +573,7 @@ static int print_pos(gpx_t *gpx, int bcOK, int csOK) { } else { if (gpx->option.vbs >= 3) { - fprintf(stdout, "[%3d]", gpx->frame_bytes[pos_Cnt]); + fprintf(stdout, "[%3d]", gpx->frame_bytes[pos_CNT]); fprintf(stdout, " (W %d) ", gpx->week); } fprintf(stdout, "%s ", weekday[gpx->wday]); @@ -629,6 +632,7 @@ static int print_frame(gpx_t *gpx, int pos) { fprintf(stdout, col_FRTXT); for (i = 0; i < FRAME_LEN+gpx->auxlen; i++) { byte = gpx->frame_bytes[i]; + if (i == 1) fprintf(stdout, col_Mtype); 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); @@ -637,6 +641,8 @@ static int print_frame(gpx_t *gpx, int pos) { 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_SN) && (i < pos_SN+3)) fprintf(stdout, col_SN); + if (i == pos_CNT) fprintf(stdout, col_CNT); 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); diff --git a/demod/mod/meisei100mod.c b/demod/mod/meisei100mod.c index 128eccf..6397c5a 100644 --- a/demod/mod/meisei100mod.c +++ b/demod/mod/meisei100mod.c @@ -212,6 +212,7 @@ int main(int argc, char **argv) { int option_iq = 0; int option_lp = 0; int option_dc = 0; + int option_softin = 0; int option_pcmraw = 0; int sel_wavch = 0; int wavloaded = 0; @@ -267,6 +268,8 @@ int main(int argc, char **argv) { pcm_t pcm = {0}; dsp_t dsp = {0}; //memset(&dsp, 0, sizeof(dsp)); + hdb_t hdb = {0}; + gpx_t gpx = {0}; @@ -308,8 +311,9 @@ int main(int argc, char **argv) { } else return -1; } - else if ( (strcmp(*argv, "--ch2") == 0) ) { sel_wavch = 1; } // right channel (default: 0=left) - else if ( (strcmp(*argv, "--ths") == 0) ) { + else if (strcmp(*argv, "--ch2") == 0) { sel_wavch = 1; } // right channel (default: 0=left) + else if (strcmp(*argv, "--softin") == 0) { option_softin = 1; } // float32 soft input + else if (strcmp(*argv, "--ths") == 0) { ++argv; if (*argv) { thres = atof(*argv); @@ -378,63 +382,98 @@ int main(int argc, char **argv) { if (!wavloaded) fp = stdin; - if (option_iq == 0 && option_pcmraw) { - fclose(fp); - fprintf(stderr, "error: raw data not IQ\n"); - return -1; + #ifdef EXT_FSK + if (!option_softin) { + option_softin = 1; + fprintf(stderr, "reading float32 soft symbols\n"); } - if (option_iq) sel_wavch = 0; + #endif - pcm.sel_ch = sel_wavch; - if (option_pcmraw == 0) { - k = read_wav_header(&pcm, fp); - if ( k < 0 ) { + if (!option_softin) { + + if (option_iq == 0 && option_pcmraw) { fclose(fp); - fprintf(stderr, "error: wav header\n"); + 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; + } + } + + symlen = 1; + + // 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; + dsp._spb = dsp.sps*symlen; + dsp.hdr = rawheader; + dsp.hdrlen = strlen(rawheader); + dsp.BT = 1.2; // bw/time (ISI) // 1.0..2.0 + dsp.h = 2.4; // 2.8 + dsp.opt_iq = option_iq; + dsp.opt_lp = option_lp; + dsp.lpIQ_bw = 16e3; // IF lowpass bandwidth + dsp.lpFM_bw = 4e3; // 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); + } + + + k = init_buffers(&dsp); + if ( k < 0 ) { + fprintf(stderr, "error: init buffers\n"); + return -1; + } + + bitofs += shift; + } + else { + // init circular header bit buffer + hdb.hdr = rawheader; + hdb.len = strlen(rawheader); + //hdb.thb = 1.0 - 3.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen + hdb.bufpos = -1; + hdb.buf = NULL; + /* + calloc(hdb.len, sizeof(char)); + if (hdb.buf == NULL) { + fprintf(stderr, "error: malloc\n"); + return -1; + } + */ + hdb.ths = 0.8; // caution/test false positive + hdb.sbuf = calloc(hdb.len, sizeof(float)); + if (hdb.sbuf == NULL) { + fprintf(stderr, "error: malloc\n"); return -1; } } - symlen = 1; - - // 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; - dsp._spb = dsp.sps*symlen; - dsp.hdr = rawheader; - dsp.hdrlen = strlen(rawheader); - dsp.BT = 1.2; // bw/time (ISI) // 1.0..2.0 - dsp.h = 2.4; // 2.8 - dsp.opt_iq = option_iq; - dsp.opt_lp = option_lp; - dsp.lpIQ_bw = 16e3; // IF lowpass bandwidth - dsp.lpFM_bw = 4e3; // 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); - } - - k = init_buffers(&dsp); - if ( k < 0 ) { - fprintf(stderr, "error: init buffers\n"); - return -1; - }; if (option_ecc) { rs_init_BCH64(&gpx.RS); @@ -442,14 +481,16 @@ int main(int argc, char **argv) { gpx.sn = -1; - bitofs += shift; - while ( 1 ) { - // FM-audio: - header_found = find_header(&dsp, thres, 1, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0 - _mv = dsp.mv; + if (option_softin) { + header_found = find_softbinhead(fp, &hdb, &_mv); + } + else { // FM-audio: + header_found = find_header(&dsp, thres, 1, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0 + _mv = dsp.mv; + } if (header_found == EOF) break; @@ -462,9 +503,14 @@ int main(int argc, char **argv) { while (bitpos < RAWBITFRAME_LEN/2-RAWHEADLEN) { // 2*600-48 - - bitQ = read_slbit(&dsp, &bit, 0, bitofs, bitpos, -1, 0); // symlen=1 - + if (option_softin) { + float s = 0.0; + bitQ = f32soft_read(fp, &s); + if (bitQ != EOF) bit = (s>=0.0); // no soft decoding + } + else { + bitQ = read_slbit(&dsp, &bit, 0, bitofs, bitpos, -1, 0); // symlen=1 + } if (bitQ == EOF) { break; } gpx.frame_rawbits[bitpos] = 0x30 + bit; @@ -768,7 +814,11 @@ int main(int argc, char **argv) { printf("\n"); - free_buffers(&dsp); + + if (!option_softin) free_buffers(&dsp); + else { + if (hdb.buf) { free(hdb.buf); hdb.buf = NULL; } + } fclose(fp); diff --git a/demod/mod/rs41mod.c b/demod/mod/rs41mod.c index ab66392..2a01517 100644 --- a/demod/mod/rs41mod.c +++ b/demod/mod/rs41mod.c @@ -87,6 +87,7 @@ typedef struct { float T; float RH; ui32_t crc; ui8_t frame[FRAME_LEN]; + ui8_t dfrm[FRAME_LEN]; ui8_t calibytes[51*16]; ui8_t calfrchk[51]; float ptu_Rf1; // ref-resistor f1 (750 Ohm) @@ -1006,7 +1007,27 @@ static int rs41_ecc(gpx_t *gpx, int frmlen) { errors2 = rs_decode(&gpx->RS, cw2, err_pos2, err_val2); - if (gpx->option.ecc == 2 && (errors1 < 0 || errors2 < 0)) + if (gpx->option.ecc >= 2) // option_softin: mark weak dfrm[] + { // 2nd pass + if (errors1 < 0) { + for (i = 0; i < frmlen/2; i++) gpx->frame[2*i] ^= gpx->dfrm[2*i]; + for (i = 0; i < rs_K; i++) cw1[rs_R+i] = gpx->frame[cfg_rs41.msgpos+2*i ]; + errors1 = rs_decode(&gpx->RS, cw1, err_pos1, err_val1); + if (errors1 < 0) { + for (i = 0; i < frmlen/2; i++) gpx->frame[2*i] ^= gpx->dfrm[2*i]; + } + } + if (errors2 < 0) { + for (i = 0; i < frmlen/2; i++) gpx->frame[2*i+1] ^= gpx->dfrm[2*i+1]; + for (i = 0; i < rs_K; i++) cw2[rs_R+i] = gpx->frame[cfg_rs41.msgpos+2*i+1]; + errors2 = rs_decode(&gpx->RS, cw2, err_pos2, err_val2); + if (errors2 < 0) { + for (i = 0; i < frmlen/2; i++) gpx->frame[2*i+1] ^= gpx->dfrm[2*i+1]; + } + } + } + + if (gpx->option.ecc >= 2 && (errors1 < 0 || errors2 < 0)) { // 2nd pass gpx->frame[pos_FRAME] = (pck_FRAME>>8)&0xFF; gpx->frame[pos_FRAME+1] = pck_FRAME&0xFF; gpx->frame[pos_PTU] = (pck_PTU >>8)&0xFF; gpx->frame[pos_PTU +1] = pck_PTU &0xFF; @@ -1032,6 +1053,14 @@ static int rs41_ecc(gpx_t *gpx, int frmlen) { errors2 = rs_decode(&gpx->RS, cw2, err_pos2, err_val2); } + if (gpx->option.ecc >= 3) + { // 3nd pass: erasures ... + if (errors1 < 0) { + } + if (errors2 < 0) { + } + } + // Wenn Fehler im 00-padding korrigiert wurden, // war entweder der frame zu kurz, oder @@ -1448,7 +1477,7 @@ static void print_frame(gpx_t *gpx, int len) { } if (gpx->option.ecc) { if (ec >= 0) fprintf(stdout, " [OK]"); else fprintf(stdout, " [NO]"); - if (gpx->option.ecc /*== 2*/) { + if (gpx->option.ecc /*>= 2*/) { if (ec > 0) fprintf(stdout, " (%d)", ec); if (ec < 0) { if (ec == -1) fprintf(stdout, " (-+)"); @@ -1467,69 +1496,6 @@ static void print_frame(gpx_t *gpx, int len) { /* -------------------------------------------------------------------------- */ -// header bit buffer -typedef struct { - char *hdr; - char *buf; - char len; - int bufpos; - float ths; -} hdb_t; - -static float cmp_hdb(hdb_t *hdb) { // bit-errors? - int i, j; - int headlen = hdb->len; - int berrs1 = 0, berrs2 = 0; - - i = 0; - j = hdb->bufpos; - while (i < headlen) { - if (j < 0) j = headlen-1; - if (hdb->buf[j] != hdb->hdr[headlen-1-i]) berrs1 += 1; - j--; - i++; - } - - i = 0; - j = hdb->bufpos; - while (i < headlen) { - if (j < 0) j = headlen-1; - if ((hdb->buf[j]^0x01) != hdb->hdr[headlen-1-i]) berrs2 += 1; - j--; - i++; - } - - if (berrs2 < berrs1) return (-headlen+berrs2)/(float)headlen; - else return ( headlen-berrs1)/(float)headlen; - - return 0; -} - -static int find_binhead(FILE *fp, hdb_t *hdb, float *score) { - int bit; - int headlen = hdb->len; - float mv; - - //*score = 0.0; - - while ( (bit = fgetc(fp)) != EOF ) - { - bit &= 1; - - hdb->bufpos = (hdb->bufpos+1) % headlen; - hdb->buf[hdb->bufpos] = 0x30 | bit; // Ascii - - mv = cmp_hdb(hdb); - if ( fabs(mv) > hdb->ths ) { - *score = mv; - return 1; - } - } - - return EOF; -} - - int main(int argc, char *argv[]) { //int option_inv = 0; // invertiert Signal @@ -1538,6 +1504,7 @@ int main(int argc, char *argv[]) { int option_lp = 0; int option_dc = 0; int option_bin = 0; + int option_softin = 0; int option_pcmraw = 0; int wavloaded = 0; int sel_wavch = 0; // audio channel: left @@ -1554,12 +1521,16 @@ int main(int argc, char *argv[]) { byte_count = FRAMESTART; int bit, byte; int bitQ; + int difbyte = 0; + hsbit_t hsbit, hsbit1; int header_found = 0; float thres = 0.7; // dsp.mv threshold float _mv = 0.0; + float lpIQ_bw = 7.4e3; + int symlen = 1; int bitofs = 2; // +0 .. +3 int shift = 0; @@ -1608,12 +1579,14 @@ int main(int argc, char *argv[]) { } else if (strcmp(*argv, "--ecc" ) == 0) { gpx.option.ecc = 1; } else if (strcmp(*argv, "--ecc2") == 0) { gpx.option.ecc = 2; } + else if (strcmp(*argv, "--ecc3") == 0) { gpx.option.ecc = 3; } else if (strcmp(*argv, "--sat") == 0) { gpx.option.sat = 1; } else if (strcmp(*argv, "--ptu") == 0) { gpx.option.ptu = 1; } else if (strcmp(*argv, "--silent") == 0) { gpx.option.slt = 1; } else if (strcmp(*argv, "--ch2") == 0) { sel_wavch = 1; } // right channel (default: 0=left) else if (strcmp(*argv, "--auto") == 0) { gpx.option.aut = 1; } - else if (strcmp(*argv, "--bin") == 0) { option_bin = 1; } // bit/byte binary input + else if (strcmp(*argv, "--bin") == 0) { option_bin = 1; } // bit/byte binary input + else if (strcmp(*argv, "--softin") == 0) { option_softin = 1; } // float32 soft input else if (strcmp(*argv, "--ths") == 0) { ++argv; if (*argv) { @@ -1644,6 +1617,14 @@ int main(int argc, char *argv[]) { option_iq = 5; } else if (strcmp(*argv, "--lp") == 0) { option_lp = 1; } // IQ lowpass + else if (strcmp(*argv, "--lpbw") == 0) { // IQ lowpass BW / kHz + double bw = 0.0; + ++argv; + if (*argv) bw = atof(*argv); + else return -1; + if (bw > 4.6 && bw < 24.0) lpIQ_bw = bw*1e3; + option_lp = 1; + } else if (strcmp(*argv, "--dc") == 0) { option_dc = 1; } else if (strcmp(*argv, "--min") == 0) { option_min = 1; @@ -1694,9 +1675,16 @@ int main(int argc, char *argv[]) { memcpy(gpx.frame, rs41_header_bytes, sizeof(rs41_header_bytes)); // 8 header bytes + #ifdef EXT_FSK + if (!option_bin && !option_softin) { + option_softin = 1; + fprintf(stderr, "reading float32 soft symbols\n"); + } + #endif + if (!rawhex) { - if (!option_bin) { + if (!option_bin && !option_softin) { if (option_iq == 0 && option_pcmraw) { fclose(fp); @@ -1736,7 +1724,7 @@ int main(int argc, char *argv[]) { dsp.h = 0.6; //0.7; // 0.7..0.8? modulation index abzgl. BT dsp.opt_iq = option_iq; dsp.opt_lp = option_lp; - dsp.lpIQ_bw = 8e3; // IF lowpass bandwidth + dsp.lpIQ_bw = lpIQ_bw; // 7.4e3 (6e3..8e3) // IF lowpass bandwidth dsp.lpFM_bw = 6e3; // FM audio lowpass dsp.opt_dc = option_dc; dsp.opt_IFmin = option_min; @@ -1744,37 +1732,48 @@ int main(int argc, char *argv[]) { if ( dsp.sps < 8 ) { fprintf(stderr, "note: sample rate low (%.1f sps)\n", dsp.sps); } + + + k = init_buffers(&dsp); // BT=0.5 (IQ-Int: BT > 0.5 ?) + if ( k < 0 ) { + fprintf(stderr, "error: init buffers\n"); + return -1; + } + + //if (option_iq >= 2) bitofs += 1; // FM: +1 , IQ: +2 + bitofs += shift; } else { + if (option_bin && option_softin) option_bin = 0; // init circular header bit buffer hdb.hdr = rs41_header; hdb.len = strlen(rs41_header); - hdb.ths = 1.0 - 3.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen + hdb.thb = 1.0 - 3.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen hdb.bufpos = -1; hdb.buf = calloc(hdb.len, sizeof(char)); if (hdb.buf == NULL) { fprintf(stderr, "error: malloc\n"); return -1; } + hdb.ths = 0.7; // caution/test false positive + hdb.sbuf = calloc(hdb.len, sizeof(float)); + if (hdb.sbuf == NULL) { + fprintf(stderr, "error: malloc\n"); + return -1; + } } - k = init_buffers(&dsp); // BT=0.5 (IQ-Int: BT > 0.5 ?) - if ( k < 0 ) { - fprintf(stderr, "error: init buffers\n"); - return -1; - }; - - //if (option_iq >= 2) bitofs += 1; // FM: +1 , IQ: +2 - bitofs += shift; - while ( 1 ) { if (option_bin) { header_found = find_binhead(fp, &hdb, &_mv); } + else if (option_softin) { + header_found = find_softbinhead(fp, &hdb, &_mv); + } else { // FM-audio: - header_found = find_header(&dsp, thres, 3, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0 + header_found = find_header(&dsp, thres, 4, bitofs, dsp.opt_dc); // optional 2nd pass: dc=0 _mv = dsp.mv; } if (header_found == EOF) break; @@ -1790,6 +1789,7 @@ int main(int argc, char *argv[]) { byte_count = FRAMESTART; bitpos = 0; // byte_count*8-HEADLEN b8pos = 0; + difbyte = 0; while ( byte_count < FRAME_LEN ) { @@ -1797,14 +1797,21 @@ int main(int argc, char *argv[]) { bitQ = fgetc(fp); if (bitQ != EOF) bit = bitQ & 0x1; } + else if (option_softin) { + float s = 0.0; + bitQ = f32soft_read(fp, &s); + if (bitQ != EOF) bit = (s>=0.0); // no soft decoding + } else { - if (option_iq >= 2) { - float bl = -1; - if (option_iq > 2) bl = 1.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, 0); + float bl = -1; + if (option_iq > 2) bl = 2.0; + //bitQ = read_slbit(&dsp, &bit, 0, bitofs, bitpos, bl, 0); // symlen=1 + bitQ = read_softbit2p(&dsp, &hsbit, 0, bitofs, bitpos, bl, 0, &hsbit1); // symlen=1 + bit = hsbit.hb; + if (gpx.option.ecc == 3) bit = (hsbit.sb+hsbit1.sb)>=0; + + if (bitpos < FRAME_LEN*BITS && hsbit.sb*hsbit1.sb < 0) { + difbyte |= 1< 0.5 ?) + if ( k < 0 ) { + fprintf(stderr, "error: init buffers\n"); + return -1; + }; + + bitofs += shift; + } + else { + // init circular header bit buffer + hdb.hdr = rs92_rawheader; + hdb.len = strlen(rs92_rawheader); + //hdb.thb = 1.0 - 3.1/(float)hdb.len; // 1.0-max_bit_errors/hdrlen + hdb.bufpos = -1; + hdb.buf = NULL; + /* + calloc(hdb.len, sizeof(char)); + if (hdb.buf == NULL) { + fprintf(stderr, "error: malloc\n"); + return -1; + } + */ + // caution ths=0.7: -3 byte offset, false positive + // 2A 2A 2A 2A 2A 10|65 10 .. + // header sync could be extended into the frame + hdb.ths = 0.8; + hdb.sbuf = calloc(hdb.len, sizeof(float)); + if (hdb.sbuf == NULL) { + fprintf(stderr, "error: malloc\n"); return -1; } } - // rs92-sgp: BT=0.5, h=1.0 ? - 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 = symlen; - dsp._spb = dsp.sps*symlen; - dsp.hdr = rs92_rawheader; - dsp.hdrlen = strlen(rs92_rawheader); - dsp.BT = 0.5; // bw/time (ISI) // 0.3..0.5 - dsp.h = 0.8; // 1.0 modulation index abzgl. BT - dsp.opt_iq = option_iq; - dsp.opt_lp = option_lp; - dsp.lpIQ_bw = 8e3; // IF lowpass bandwidth - dsp.lpFM_bw = 6e3; // FM audio lowpass - dsp.opt_dc = option_dc; - dsp.opt_IFmin = option_min; - if (gpx.option.ngp) { // L-band rs92-ngp - dsp.h = 3.8; // RS92-NGP: 1680/400=4.2, 4.2*0.9=3.8=4.75*0.8 - dsp.lpIQ_bw = 32e3; // IF lowpass bandwidth // 32e3=4.2*7.6e3 - } - - if ( dsp.sps < 8 ) { - fprintf(stderr, "note: sample rate low (%.1f sps)\n", dsp.sps); - } - - - k = init_buffers(&dsp); // BT=0.5 (IQ-Int: BT > 0.5 ?) - if ( k < 0 ) { - fprintf(stderr, "error: init buffers\n"); - return -1; - }; - - bitofs += shift; - - while ( 1 ) { - - header_found = find_header(&dsp, thres, 3, bitofs, dsp.opt_dc); - _mv = dsp.mv; + while ( 1 ) + { + if (option_softin) { + for (k = 0; k < hdb.len; k++) hdb.sbuf[k] = 0.0; + header_found = find_softbinhead(fp, &hdb, &_mv); + } + else { + header_found = find_header(&dsp, thres, 3, bitofs, dsp.opt_dc); + _mv = dsp.mv; + } if (header_found == EOF) break; @@ -1560,9 +1608,25 @@ int main(int argc, char *argv[]) { b8pos = 0; while ( byte_count < FRAME_LEN ) { - float bl = -1; - if (option_iq > 2) bl = 4.0; - bitQ = read_slbit(&dsp, &bit, 0/*gpx.option.inv*/, bitofs, bitpos, bl, spike); + + if (option_softin) { + float s1 = 0.0; + float s2 = 0.0; + float s = 0.0; + bitQ = f32soft_read(fp, &s1); + if (bitQ != EOF) { + bitQ = f32soft_read(fp, &s2); + if (bitQ != EOF) { + s = s2-s1; // integrate both symbols // only 2nd Manchester symbol: s2 + bit = (s>=0.0); // no soft decoding + } + } + } + else { + float bl = -1; + if (option_iq > 2) bl = 4.0; + bitQ = read_slbit(&dsp, &bit, 0, bitofs, bitpos, bl, spike); // symlen=2 + } if ( bitQ == EOF) break; if (gpx.option.inv) bit ^= 1; @@ -1586,9 +1650,13 @@ int main(int argc, char *argv[]) { } - free_buffers(&dsp); + if (!option_softin) free_buffers(&dsp); + else { + if (hdb.buf) { free(hdb.buf); hdb.buf = NULL; } + } if (gpx.gps.ephs) free(gpx.gps.ephs); + fclose(fp); return 0;