commit 59bf74f18f34fc5011d568d1ab652d84137b2f9b Author: Harald Welte Date: Fri Oct 10 16:45:05 2014 +0200 initial check-in of Mode-S / ADS-B test data generator diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1222362 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +top_block.py +adsb_gen +adsb.byte diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8acb6bf --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ + + +all: adsb_gen + +adsb_gen: adsb_gen.c + $(CC) -Wall $^ -o $@ -losmocore + +clean: + rm -f adsb_gen adsb_gen.o diff --git a/README b/README new file mode 100644 index 0000000..037b0c8 --- /dev/null +++ b/README @@ -0,0 +1,37 @@ +Osmocom ADS-B generator/transmitter +(C) 2014 by Harald Welte +====================================================================== +adsb_gen - ADS-B / Mode-S transmitter + +WARNING: ONLY USE THIS TOOL WITH DIRECT COAXIAL CABLE CONNECTION BETWEEN +TRANSMITTER AND RECEIVER OR INSIDE A SHIELDED CHAMBER. ADS-B OPERATES ON +LICENSED SPECTRUM AND THE AUTHOR IS NOT RESPONSIBLE FOR IMPROPER USE OF +THIS TOOL. + +This is a small toolset for generating Mode-S / ADS-B test data, +intended for testing ADS-B receivers. This includes production testing +but also comparison of various receivers against each other in terms of +sensitivity. + +This is very early PoC code and can probably do with lots of +improvements, particularly related to the modulation depth, transmit +power, etc. + +The idea is as follows: + +* provide a text file with ADS-B sentences in regular encoding like + '*8D48415F600796758E', one sentence on every line. + +* call adsb_gen with the synax '[-p pause_usec] ' + where infname is the name of the ASCII input file and outfname is the + binary. + +* the above will generate the output file whihc contains the coded ADS-B + data at one unsigned byte per bit at 2MS/s sample rate. + +* you can use the file_to_usrp2.grc gnuradio companion block to actually + send this file in an endless loop. + +* the '-p' parameter can be used to configure the gap between subsequent + ADS-B sentences (specified in microseconds). The default is 1s. + diff --git a/adsb_gen.c b/adsb_gen.c new file mode 100644 index 0000000..93b363f --- /dev/null +++ b/adsb_gen.c @@ -0,0 +1,216 @@ +/* (C) 2014 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define PREAMBLE_LEN 8 +#define SHORT_LEN 56 +#define LONG_LEN 112 + +#define OUTBUF_SIZE ((LONG_LEN+PREAMBLE_LEN)*2) + +#define SPACE 0xff + +static const ubit_t preamble[PREAMBLE_LEN] = { 1, 1, SPACE, 0, 0, SPACE, SPACE, SPACE }; + +/* encode Pulse Position Modulation */ +static int ppm_encode(ubit_t *out, const ubit_t *in, unsigned int num_in) +{ + int i; + int j = 0; + + for (i = 0; i < num_in; i++) { + switch (in[i]) { + case 0: + out[j++] = 0; + out[j++] = 1; + break; + case 1: + out[j++] = 1; + out[j++] = 0; + break; + case SPACE: + out[j++] = 0; + out[j++] = 0; + break; + default: + return -1; + } + } + return 0; +} + +int modes_encode_from_bin(ubit_t *out, const pbit_t *bin, unsigned int num_bits) +{ + ubit_t bitbuf[PREAMBLE_LEN+LONG_LEN]; + int rc, out_len; + + printf("from_bin(num_bits=%u)\n", num_bits); + + /* assemble the un-encoded bits of the entire message */ + memcpy(bitbuf, preamble, sizeof(preamble)); + rc = osmo_pbit2ubit(bitbuf+sizeof(preamble), bin, num_bits); + + printf("sym=%s\n", osmo_ubit_dump(bitbuf, num_bits+sizeof(preamble))); + + /* encode using pulse-position modulation */ + rc = ppm_encode(out, bitbuf, num_bits+sizeof(preamble)); + if (rc < 0) { + fprintf(stderr, "Error during ppm_encode\n"); + return rc; + } + + out_len = 2*(num_bits + sizeof(preamble)); + + printf("out=%s\n", osmo_ubit_dump(out, out_len)); + + return out_len; +} + +int modes_encode_from_ascii(ubit_t *out, const char *in) +{ + char ascbuf[LONG_LEN*2]; + ubit_t binbuf[LONG_LEN]; + int str_len = strlen(in); + int num_bytes = (str_len - 2) / 2; + int rc; + + /* sanity checking */ + if (str_len > LONG_LEN*2 + 2) { + fprintf(stderr, "String too long!\n"); + return -1; + } + + if (in[0] != '*' || in[strlen(in)-1] != ';') { + fprintf(stderr, "string `%s' not in correct format\n", in); + return -2; + } + + /* copy string, skipping heading '*' and trailing ';' */ + memset(ascbuf, 0, sizeof(ascbuf)); + strncpy(ascbuf, in+1, str_len-2); + + /* convert from hex to binary */ + rc = osmo_hexparse(ascbuf, binbuf, sizeof(binbuf)); + if (rc < 0) { + fprintf(stderr, "error during hexparse of `%s'\n", ascbuf); + return rc; + } + + /* encode the actual binary */ + return modes_encode_from_bin(out, binbuf, num_bytes*8); +} + +static int outfd; + +/* write a frame to the output file */ +static int put_frame(const ubit_t *frame, unsigned int num_bits) +{ + return write(outfd, frame, num_bits); +} + +/* write a pause to the output file (given duration in usec) */ +static int put_pause(unsigned int usec) +{ + uint8_t buf[100000]; + unsigned int i, num_samples = usec * 2; + unsigned int written = 0; + + memset(buf, 0, sizeof(buf)); + + for (i = 0; i < num_samples; i += written) { + unsigned int pending = num_samples - i; + int rc; + + written = pending; + if (written > sizeof(buf)) + written = sizeof(buf); + + rc = write(outfd, buf, written); + if (rc < written) + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int rc; + char line[255]; + uint8_t outbuf[OUTBUF_SIZE]; + char *infname, *outfname; + FILE *infile; + int opt, pause_usec = 1000000; + + while ((opt = getopt(argc, argv, "p:")) != -1) { + switch (opt) { + case 'p': + pause_usec = atoi(optarg); + break; + default: + exit(2); + } + } + + if (argc <= optind+1) { + fprintf(stderr, "Usage: %s [-p pause_usec] \n", argv[0]); + exit(2); + } + + infname = argv[optind]; + outfname = argv[optind+1]; + + infile = fopen(infname, "r"); + if (!infile) + exit(2); + + outfd = open(outfname, O_CREAT|O_WRONLY|O_TRUNC, 0660); + if (outfd < 0) + exit(2); + + while (fgets(line, sizeof(line), infile)) { + if (!strlen(line)) + continue; + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + printf("IN: %s\n", line); + + rc = modes_encode_from_ascii(outbuf, line); + if (rc < 0) + exit(-rc); + put_frame(outbuf, rc); + + put_pause(pause_usec); + } + + fclose(infile); + close(outfd); + + exit(0); +} diff --git a/examples/in.txt b/examples/in.txt new file mode 100644 index 0000000..2262699 --- /dev/null +++ b/examples/in.txt @@ -0,0 +1,4 @@ +*8D48415F99048D82E83205CFFD62; +*8D48415F600796758E9803F081E5; +*8D48415F202CC371E32E6035A638; +*024100B9613427; diff --git a/file_tx_usrp2.grc b/file_tx_usrp2.grc new file mode 100644 index 0000000..ac24c7e --- /dev/null +++ b/file_tx_usrp2.grc @@ -0,0 +1,1392 @@ + + + + Fri Oct 10 14:51:33 2014 + + options + + id + top_block + + + _enabled + True + + + title + + + + author + + + + description + + + + window_size + 1280, 1024 + + + generate_options + wx_gui + + + category + Custom + + + run_options + prompt + + + run + True + + + max_nouts + 0 + + + realtime_scheduling + + + + alias + + + + _coordinate + (10, 10) + + + _rotation + 0 + + + + variable + + id + samp_rate + + + _enabled + True + + + value + 2000000 + + + alias + + + + _coordinate + (10, 170) + + + _rotation + 0 + + + + analog_sig_source_x + + id + analog_sig_source_x_0_0 + + + _enabled + True + + + type + complex + + + samp_rate + samp_rate + + + waveform + analog.GR_COS_WAVE + + + freq + 0 + + + amp + 1 + + + offset + 0 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (496, 72) + + + _rotation + 0 + + + + uhd_usrp_sink + + id + uhd_usrp_sink_0 + + + _enabled + True + + + type + fc32 + + + otw + + + + stream_args + + + + stream_chans + [] + + + dev_addr + "" + + + dev_args + "" + + + sync + + + + clock_rate + 0.0 + + + num_mboards + 1 + + + clock_source0 + + + + time_source0 + + + + sd_spec0 + A:0 + + + clock_source1 + + + + time_source1 + + + + sd_spec1 + + + + clock_source2 + + + + time_source2 + + + + sd_spec2 + + + + clock_source3 + + + + time_source3 + + + + sd_spec3 + + + + clock_source4 + + + + time_source4 + + + + sd_spec4 + + + + clock_source5 + + + + time_source5 + + + + sd_spec5 + + + + clock_source6 + + + + time_source6 + + + + sd_spec6 + + + + clock_source7 + + + + time_source7 + + + + sd_spec7 + + + + nchan + 1 + + + samp_rate + samp_rate + + + center_freq0 + 1090000000 + + + gain0 + 0 + + + ant0 + + + + bw0 + 0 + + + center_freq1 + 0 + + + gain1 + 0 + + + ant1 + + + + bw1 + 0 + + + center_freq2 + 0 + + + gain2 + 0 + + + ant2 + + + + bw2 + 0 + + + center_freq3 + 0 + + + gain3 + 0 + + + ant3 + + + + bw3 + 0 + + + center_freq4 + 0 + + + gain4 + 0 + + + ant4 + + + + bw4 + 0 + + + center_freq5 + 0 + + + gain5 + 0 + + + ant5 + + + + bw5 + 0 + + + center_freq6 + 0 + + + gain6 + 0 + + + ant6 + + + + bw6 + 0 + + + center_freq7 + 0 + + + gain7 + 0 + + + ant7 + + + + bw7 + 0 + + + center_freq8 + 0 + + + gain8 + 0 + + + ant8 + + + + bw8 + 0 + + + center_freq9 + 0 + + + gain9 + 0 + + + ant9 + + + + bw9 + 0 + + + center_freq10 + 0 + + + gain10 + 0 + + + ant10 + + + + bw10 + 0 + + + center_freq11 + 0 + + + gain11 + 0 + + + ant11 + + + + bw11 + 0 + + + center_freq12 + 0 + + + gain12 + 0 + + + ant12 + + + + bw12 + 0 + + + center_freq13 + 0 + + + gain13 + 0 + + + ant13 + + + + bw13 + 0 + + + center_freq14 + 0 + + + gain14 + 0 + + + ant14 + + + + bw14 + 0 + + + center_freq15 + 0 + + + gain15 + 0 + + + ant15 + + + + bw15 + 0 + + + center_freq16 + 0 + + + gain16 + 0 + + + ant16 + + + + bw16 + 0 + + + center_freq17 + 0 + + + gain17 + 0 + + + ant17 + + + + bw17 + 0 + + + center_freq18 + 0 + + + gain18 + 0 + + + ant18 + + + + bw18 + 0 + + + center_freq19 + 0 + + + gain19 + 0 + + + ant19 + + + + bw19 + 0 + + + center_freq20 + 0 + + + gain20 + 0 + + + ant20 + + + + bw20 + 0 + + + center_freq21 + 0 + + + gain21 + 0 + + + ant21 + + + + bw21 + 0 + + + center_freq22 + 0 + + + gain22 + 0 + + + ant22 + + + + bw22 + 0 + + + center_freq23 + 0 + + + gain23 + 0 + + + ant23 + + + + bw23 + 0 + + + center_freq24 + 0 + + + gain24 + 0 + + + ant24 + + + + bw24 + 0 + + + center_freq25 + 0 + + + gain25 + 0 + + + ant25 + + + + bw25 + 0 + + + center_freq26 + 0 + + + gain26 + 0 + + + ant26 + + + + bw26 + 0 + + + center_freq27 + 0 + + + gain27 + 0 + + + ant27 + + + + bw27 + 0 + + + center_freq28 + 0 + + + gain28 + 0 + + + ant28 + + + + bw28 + 0 + + + center_freq29 + 0 + + + gain29 + 0 + + + ant29 + + + + bw29 + 0 + + + center_freq30 + 0 + + + gain30 + 0 + + + ant30 + + + + bw30 + 0 + + + center_freq31 + 0 + + + gain31 + 0 + + + ant31 + + + + bw31 + 0 + + + len_tag_name + + + + alias + + + + affinity + + + + _coordinate + (984, 392) + + + _rotation + 0 + + + + blocks_multiply_xx + + id + blocks_multiply_xx_0 + + + _enabled + True + + + type + complex + + + num_inputs + 2 + + + vlen + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (728, 271) + + + _rotation + 0 + + + + blocks_float_to_complex + + id + blocks_float_to_complex_0 + + + _enabled + True + + + vlen + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (496, 327) + + + _rotation + 0 + + + + analog_const_source_x + + id + analog_const_source_x_0 + + + _enabled + False + + + type + float + + + const + 0 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (272, 412) + + + _rotation + 0 + + + + wxgui_waterfallsink2 + + id + wxgui_waterfallsink2_0 + + + _enabled + False + + + type + complex + + + title + Waterfall Plot + + + samp_rate + samp_rate + + + baseband_freq + 0 + + + dynamic_range + 100 + + + ref_level + 0 + + + ref_scale + 2.0 + + + fft_size + 512 + + + fft_rate + 15 + + + average + False + + + avg_alpha + 0 + + + win + None + + + win_size + + + + grid_pos + + + + notebook + + + + freqvar + None + + + alias + + + + affinity + + + + _coordinate + (1024, 12) + + + _rotation + 0 + + + + blocks_char_to_float + + id + blocks_char_to_float_0 + + + _enabled + True + + + vlen + 1 + + + scale + 10 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (264, 324) + + + _rotation + 0 + + + + wxgui_scopesink2 + + id + wxgui_scopesink2_0 + + + _enabled + False + + + type + complex + + + title + Scope Plot + + + samp_rate + samp_rate + + + v_scale + 0 + + + v_offset + 0 + + + t_scale + 0.01 + + + ac_couple + False + + + xy_mode + False + + + num_inputs + 1 + + + win_size + + + + grid_pos + + + + notebook + + + + trig_mode + wxgui.TRIG_MODE_AUTO + + + y_axis_label + Counts + + + alias + + + + affinity + + + + _coordinate + (1016, 199) + + + _rotation + 0 + + + + blocks_short_to_char + + id + blocks_short_to_char_0 + + + _enabled + False + + + vlen + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (256, 608) + + + _rotation + 0 + + + + analog_sig_source_x + + id + analog_sig_source_x_0 + + + _enabled + False + + + type + short + + + samp_rate + samp_rate + + + waveform + analog.GR_SQR_WAVE + + + freq + 1000000 + + + amp + 16383 + + + offset + 0 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (48, 584) + + + _rotation + 0 + + + + blocks_file_source + + id + blocks_file_source_0 + + + _enabled + True + + + file + ./adsb.byte + + + type + byte + + + repeat + True + + + vlen + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (40, 349) + + + _rotation + 0 + + + + blocks_multiply_xx_0 + uhd_usrp_sink_0 + 0 + 0 + + + analog_sig_source_x_0_0 + blocks_multiply_xx_0 + 0 + 0 + + + blocks_multiply_xx_0 + wxgui_waterfallsink2_0 + 0 + 0 + + + blocks_multiply_xx_0 + wxgui_scopesink2_0 + 0 + 0 + + + blocks_float_to_complex_0 + blocks_multiply_xx_0 + 0 + 1 + + + blocks_char_to_float_0 + blocks_float_to_complex_0 + 0 + 0 + + + analog_const_source_x_0 + blocks_float_to_complex_0 + 0 + 1 + + + analog_sig_source_x_0 + blocks_short_to_char_0 + 0 + 0 + + + blocks_short_to_char_0 + blocks_char_to_float_0 + 0 + 0 + + + blocks_file_source_0 + blocks_char_to_float_0 + 0 + 0 + +