From 7c3b3de7de805183d075e1144f9234e5e7f8b4ea Mon Sep 17 00:00:00 2001 From: Pieter Robyns Date: Thu, 7 Sep 2017 17:05:12 +0200 Subject: [PATCH] - Update README.md - New installation method via Docker - Updated examples in apps/ --- README.md | 93 ++++++++++++--------- apps/lora_receive_file.grc | 14 ++-- apps/lora_receive_file.py | 4 +- apps/lora_receive_realtime.grc | 141 ++++++++++++++------------------ apps/lora_receive_realtime.py | 117 ++++++++++---------------- docker/Dockerfile | 34 ++++++++ docker/docker_build_image.sh | 5 ++ docker/docker_run_grlora.sh | 7 ++ docker/mkimage-arch-pacman.conf | 91 +++++++++++++++++++++ docker/mkimage-arch.sh | 127 ++++++++++++++++++++++++++++ examples/screenshot.png | Bin 9589 -> 30378 bytes 11 files changed, 431 insertions(+), 202 deletions(-) create mode 100644 docker/Dockerfile create mode 100755 docker/docker_build_image.sh create mode 100755 docker/docker_run_grlora.sh create mode 100644 docker/mkimage-arch-pacman.conf create mode 100755 docker/mkimage-arch.sh diff --git a/README.md b/README.md index 8c2674b..0b34a46 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,107 @@ gr-lora [![Build status](https://api.travis-ci.org/rpp0/gr-lora.svg)](https://travis-ci.org/rpp0/gr-lora) [![DOI](https://zenodo.org/badge/61641535.svg)](https://zenodo.org/badge/latestdoi/61641535) ======= -The gr-lora project aims to provide a collection of GNU Radio blocks for receiving LoRa modulated radio messages using Software Defined Radio (SDR). More information about LoRa itself can be found on [the website of the LoRa Alliance](https://www.lora-alliance.org/). +The gr-lora project aims to provide a collection of GNU Radio blocks for receiving LoRa modulated radio messages using a Software Defined Radio (SDR). More information about LoRa itself can be found on [the website of the LoRa Alliance](https://www.lora-alliance.org/). ![alt text](https://github.com/rpp0/gr-lora/blob/master/examples/screenshot.png "gr-lora example") +## Features -Update of 29th August, 2017 ---------------------------- +All features of the LoRa physical-layer modulation scheme are described in various patents and blog posts (for a good resource, see [this RevSpace page](https://revspace.nl/DecodingLora)). ```gr-lora``` supports most of these features, except for: -I'm happy to announce that as of ```gr-lora``` version 0.6, a new clock recovery algorithm has been implemented which fixes previous issues with long LoRa messages. Other components, such as whitening, detection and decoding have been improved as well (see the Git log for more details). Given a clear signal, the decoding accuracy is now close to 100% for all SFs, and I therefore consider LoRa fully reverse engineered. Future updates will focus on improving the performance and minor details of the specification. +- CRC checks of the payload and header +- Decoding multiple channels simultaneously +This library was primarily tested with a USRP B201 as receiver and Microchip RN2483 as transmitter. If you encounter an issue with your particular setup, feel free to let me know in the 'Issues' section of this repository. -Usage ------ -See the LICENSE file and top of the source files for the license of this project. If you are working on a research topic or project that involves the usage of ```gr-lora``` or its algorithms, we would appreciate it if you could acknowledge us. We are currently working on a full writeup of the decoder, but in the meantime, you can cite this repository as follows: +### Update of 29th August, 2017 -``` -Pieter Robyns, Peter Quax, Wim Lamotte, William Thenaers. (2017). gr-lora: An efficient LoRa decoder for GNU Radio. Zenodo. 10.5281/zenodo.853201 -``` +I'm happy to announce that as of ```gr-lora``` version 0.6, a new clock recovery algorithm has been implemented which fixes previous issues with long LoRa messages. Other components, such as whitening, detection and decoding have been improved as well (see the Git log for more details). Given a clear signal, the decoding accuracy is now [close to 100% for all SFs](https://github.com/rpp0/gr-lora/tree/master/docs/test-results), and I therefore consider LoRa fully reverse engineered. Future updates will focus on improving the performance and minor details of the specification. -Features --------- +## Attribution -All features of the LoRa physical-layer modulation scheme are described in various patents and blog posts (for a good resource, see [this RevSpace page](https://revspace.nl/DecodingLora)). ```gr-lora``` supports most of these features, except for: +If you are working on a research topic or project that involves the usage of ```gr-lora``` or its algorithms, we would appreciate it if you could acknowledge us. We are currently working on a full writeup of the decoder, but in the meantime, you can cite this repository as follows: -- CRC checks of the payload and header -- Decoding multiple channels simultaneously +> Pieter Robyns, Peter Quax, Wim Lamotte, William Thenaers. (2017). gr-lora: An efficient LoRa decoder for GNU Radio. Zenodo. 10.5281/zenodo.853201 + + +## Installation + +Installing `gr-lora` is possible in two ways: either by downloading the Docker container, which contains all dependencies and `gr-lora` packaged in a single container, or by manual installation. -This library was primarily tested with a USRP B201 as receiver and Microchip RN2483 as transmitter. If you encounter an issue with your particular setup, feel free to let me know in the Issues section of this repository. +### Docker installation +To avoid installation errors and cluttering your system with the required dependencies, the best approach to install `gr-lora` is through a Docker container. Make sure the `docker` service is running, then perform the following steps: -Installation ------------- +``` +git clone https://github.com/rpp0/gr-lora.git . +cd docker/ +./docker_run_grlora.sh +``` + +The `docker_run_grlora.sh` script will download the Docker container and run it in 'privileged' mode (in order to access your SDR over USB). After that, you should get a shell inside the container: + +``` +[root@5773ed19d95d apps]# +``` -The following dependencies are required: -- numpy -- scipy -- log4cpp -- [liquid-dsp](https://github.com/jgaeddert/liquid-dsp) +See the 'Testing' section below for examples on how to use `gr-lora`. +### Manual installation -The installation procedure is the same as for any GNURadio OOT module: +If you prefer a manual installation, the following dependencies are required: `python2-numpy`, `python2-scipy`, `swig`, `cppunit`, `fftw`, `gnuradio`, `libvolk`, `log4cpp`, `cmake`, `wx`, and [`liquid-dsp`](https://github.com/jgaeddert/liquid-dsp). + +The installation procedure is the same as for any GNU Radio OOT module: ``` +git clone https://github.com/rpp0/gr-lora.git . mkdir build cd build cmake ../ # Note to Arch Linux users: add "-DCMAKE_INSTALL_PREFIX=/usr" make && sudo make install ``` -Testing -------- +## Testing and usage -To test your installation, you can download one of the sample LoRa signals at [rpp0/gr-lora-samples](https://github.com/rpp0/gr-lora-samples). Configure ```apps/lora_receive_file.py``` to use the sample and run the script. You should see the decoded sample data: +To test your installation, you can download one of the example LoRa signals at [rpp0/gr-lora-samples](https://github.com/rpp0/gr-lora-samples). Configure ```apps/lora_receive_file.py``` to use the sample and run the script. You should see the decoded sample data: ``` $ ./lora_receive_file.py -Bits per symbol: 7 +Bits (nominal) per symbol: 7 Bins per symbol: 128 Header bins per symbol: 32 Samples per symbol: 1024 Using Volk machine: avx2_64_mmx_orc -00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 b8 73 af 81 69 -88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 fd e5 af 81 69 -12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 a3 69 af 81 69 +00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 +88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 +12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 ... ``` -Alternatively, if you have a LoRa transmitter, you can configure/modify ```/examples/_examplify.py``` to generate example files and add them to ```/examples/qa_BasicTest_Data.xml``` to run them automatically with ```python/qa_BasicTest_XML.py``` (```xmltodict``` module needed). Note that this script should be run with its shell script in ```build/python```. This is to ensure compatibility with ```make test```. +Alternatively, if you have a hardware LoRa transmitter, you use ```apps/lora_receive_realtime.py``` to decode signals in real time. If you have a Microchip RN2483, you can use [python-loranode](https://github.com/rpp0/python-loranode) to easily send messages via Python. + +By default, decoded messages will be printed to the console output. However, you can use a `message_socket_sink` to forward messages to port 40868 over UDP. See the [tutorial](https://github.com/rpp0/gr-lora/wiki/Capturing-LoRa-signals-using-an-RTL-SDR-device) for more information. -Contributing ------------- +## Contributing Contributions to the project are very much appreciated! If you have an idea for improvement or noticed a bug, feel free to submit an issue. If you're up for the challenge and would like to introduce a feature yourself, we kindly invite you to submit a pull request. -Hardware --------- +## Hardware support -The following LoRa modules and SDRs were also tested and work with gr-lora: +The following LoRa modules and SDRs were tested and work with gr-lora: Transmitters: Pycom LoPy, Dragino LoRa Raspberry Pi HAT, Adafruit Feather 32u4, Microchip RN 2483 (custom board) Receivers: HackRF One, USRP B201, RTL-SDR -Changelog ---------- +## Changelog +- Version 0.6.1: Minor bug fixes and improvements. - Version 0.6 : Significantly increased decoding accuracy and clock drift correction. - Version 0.5 : Major overhaul of preamble detection and upchirp syncing - Version 0.4 : Support for all spreading factors, though SFs 11 and 12 are still slow / experimental @@ -99,3 +109,8 @@ Changelog - Version 0.2.1: Fixed some issues reported by reletreby - Version 0.2 : C++ realtime decoder, manual finetuning for correcting frequency offsets of the transmitter. - Version 0.1 : Python prototype file based decoder, SF7, CR4/8 + + +## License + +See the LICENSE file and top of the source files for the license of this project. diff --git a/apps/lora_receive_file.grc b/apps/lora_receive_file.grc index 2224763..fdeedc4 100644 --- a/apps/lora_receive_file.grc +++ b/apps/lora_receive_file.grc @@ -30,7 +30,7 @@ _coordinate - (8, 8) + (8, 12) _rotation @@ -282,7 +282,7 @@ _coordinate - (280, 77) + (184, 76) _rotation @@ -321,7 +321,7 @@ _coordinate - (16, 188) + (8, 188) _rotation @@ -372,7 +372,7 @@ _coordinate - (224, 196) + (232, 196) _rotation @@ -431,7 +431,7 @@ threshold - 0.01 + 0.002 _enabled @@ -439,7 +439,7 @@ _coordinate - (424, 168) + (416, 164) _rotation @@ -510,7 +510,7 @@ _coordinate - (424, 284) + (416, 276) _rotation diff --git a/apps/lora_receive_file.py b/apps/lora_receive_file.py index 01e47f9..810eb7b 100755 --- a/apps/lora_receive_file.py +++ b/apps/lora_receive_file.py @@ -3,7 +3,7 @@ ################################################## # GNU Radio Python Flow Graph # Title: Lora Receive File -# Generated: Tue Aug 29 13:32:47 2017 +# Generated: Thu Sep 7 15:38:03 2017 ################################################## if __name__ == '__main__': @@ -66,7 +66,7 @@ def __init__(self): peak_hold=False, ) self.Add(self.wxgui_fftsink2_1.win) - self.lora_lora_receiver_0 = lora.lora_receiver(samp_rate, capture_freq, ([target_freq]), 7, 1000000, 0.01) + self.lora_lora_receiver_0 = lora.lora_receiver(samp_rate, capture_freq, ([target_freq]), 7, 1000000, 0.002) self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True) self.blocks_file_source_0 = blocks.file_source(gr.sizeof_gr_complex*1, 'counting_cr4_sf7.cfile', True) diff --git a/apps/lora_receive_realtime.grc b/apps/lora_receive_realtime.grc index 5886629..7d94f7b 100644 --- a/apps/lora_receive_realtime.grc +++ b/apps/lora_receive_realtime.grc @@ -30,7 +30,7 @@ _coordinate - (8, 8) + (8, 12) _rotation @@ -93,7 +93,7 @@ _coordinate - (552, 13) + (544, 12) _rotation @@ -120,7 +120,7 @@ _coordinate - (456, 13) + (456, 12) _rotation @@ -159,66 +159,7 @@ value - 867.8e6 - - - - variable_slider - - comment - - - - converver - int_converter - - - value - -95 - - - _enabled - True - - - _coordinate - (968, 16) - - - _rotation - 0 - - - grid_pos - - - - id - finetune - - - label - - - - max - 150 - - - min - -150 - - - notebook - - - - num_steps - 300 - - - style - wx.SL_HORIZONTAL + 868e6 @@ -233,7 +174,7 @@ _coordinate - (800, 13) + (768, 12) _rotation @@ -252,7 +193,10 @@ variable comment - + The internal sampling rate determines the +oversampling rate. 1e6 is 8 times oversampling, +5e5 is 4 times oversampling, and so on. Set higher +for better accuracy, but worse performance. _enabled @@ -260,7 +204,7 @@ _coordinate - (376, 77) + (640, 76) _rotation @@ -268,11 +212,11 @@ id - offset + internal_sampling_rate value - -(capture_freq - target_freq) + 5e5 @@ -299,7 +243,7 @@ value - 2e6 + 1e6 @@ -314,7 +258,7 @@ _coordinate - (376, 13) + (384, 12) _rotation @@ -326,7 +270,7 @@ value - 12 + 11 @@ -341,7 +285,7 @@ _coordinate - (664, 13) + (640, 12) _rotation @@ -407,7 +351,7 @@ threshold - 0.01 + 0.002 _enabled @@ -415,7 +359,7 @@ _coordinate - (384, 168) + (392, 172) _rotation @@ -435,7 +379,7 @@ out_samp_rate - 500000 + internal_sampling_rate in_samp_rate @@ -446,6 +390,37 @@ sf + + lora_message_socket_sink + + alias + + + + comment + + + + affinity + + + + _enabled + True + + + _coordinate + (640, 208) + + + _rotation + 0 + + + id + lora_message_socket_sink_0 + + osmosdr_source @@ -666,11 +641,11 @@ _enabled - 0 + 1 _coordinate - (16, 148) + (24, 140) _rotation @@ -1881,11 +1856,11 @@ _enabled - 1 + 0 _coordinate - (8, 336) + (16, 340) _rotation @@ -2080,7 +2055,7 @@ _coordinate - (384, 292) + (392, 292) _rotation @@ -2143,6 +2118,12 @@ 10 + + lora_lora_receiver_0 + lora_message_socket_sink_0 + frames + in + osmosdr_source_0 lora_lora_receiver_0 diff --git a/apps/lora_receive_realtime.py b/apps/lora_receive_realtime.py index b45d368..6e3fd95 100755 --- a/apps/lora_receive_realtime.py +++ b/apps/lora_receive_realtime.py @@ -3,7 +3,7 @@ ################################################## # GNU Radio Python Flow Graph # Title: Lora Receive Realtime -# Generated: Tue Aug 29 13:37:16 2017 +# Generated: Thu Sep 7 15:47:58 2017 ################################################## if __name__ == '__main__': @@ -18,17 +18,15 @@ from gnuradio import eng_notation from gnuradio import gr -from gnuradio import uhd from gnuradio import wxgui from gnuradio.eng_option import eng_option from gnuradio.fft import window from gnuradio.filter import firdes from gnuradio.wxgui import fftsink2 -from gnuradio.wxgui import forms from grc_gnuradio import wxgui as grc_wxgui from optparse import OptionParser import lora -import time +import osmosdr import wx @@ -40,15 +38,14 @@ def __init__(self): ################################################## # Variables ################################################## - self.target_freq = target_freq = 868.1e6 - self.sf = sf = 12 - self.samp_rate = samp_rate = 2e6 - self.capture_freq = capture_freq = 867.8e6 + self.sf = sf = 11 + self.samp_rate = samp_rate = 1e6 self.bw = bw = 125e3 + self.target_freq = target_freq = 868.1e6 self.symbols_per_sec = symbols_per_sec = bw / (2**sf) - self.offset = offset = -(capture_freq - target_freq) + self.internal_sampling_rate = internal_sampling_rate = 5e5 self.firdes_tap = firdes_tap = firdes.low_pass(1, samp_rate, bw, 10000, firdes.WIN_HAMMING, 6.67) - self.finetune = finetune = -95 + self.capture_freq = capture_freq = 868e6 self.bitrate = bitrate = sf * (1 / (2**sf / bw)) ################################################## @@ -70,54 +67,28 @@ def __init__(self): peak_hold=False, ) self.Add(self.wxgui_fftsink2_1.win) - self.uhd_usrp_source_0 = uhd.usrp_source( - ",".join(("", "")), - uhd.stream_args( - cpu_format="fc32", - channels=range(1), - ), - ) - self.uhd_usrp_source_0.set_samp_rate(samp_rate) - self.uhd_usrp_source_0.set_center_freq(capture_freq, 0) - self.uhd_usrp_source_0.set_gain(15, 0) - self.uhd_usrp_source_0.set_antenna('RX2', 0) - self.lora_lora_receiver_0 = lora.lora_receiver(samp_rate, capture_freq, ([target_freq]), sf, 500000, 0.01) - _finetune_sizer = wx.BoxSizer(wx.VERTICAL) - self._finetune_text_box = forms.text_box( - parent=self.GetWin(), - sizer=_finetune_sizer, - value=self.finetune, - callback=self.set_finetune, - label='finetune', - converter=forms.int_converter(), - proportion=0, - ) - self._finetune_slider = forms.slider( - parent=self.GetWin(), - sizer=_finetune_sizer, - value=self.finetune, - callback=self.set_finetune, - minimum=-150, - maximum=150, - num_steps=300, - style=wx.SL_HORIZONTAL, - cast=int, - proportion=1, - ) - self.Add(_finetune_sizer) + self.osmosdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + '' ) + self.osmosdr_source_0.set_sample_rate(samp_rate) + self.osmosdr_source_0.set_center_freq(capture_freq, 0) + self.osmosdr_source_0.set_freq_corr(0, 0) + self.osmosdr_source_0.set_dc_offset_mode(0, 0) + self.osmosdr_source_0.set_iq_balance_mode(0, 0) + self.osmosdr_source_0.set_gain_mode(False, 0) + self.osmosdr_source_0.set_gain(10, 0) + self.osmosdr_source_0.set_if_gain(20, 0) + self.osmosdr_source_0.set_bb_gain(20, 0) + self.osmosdr_source_0.set_antenna('', 0) + self.osmosdr_source_0.set_bandwidth(0, 0) + + self.lora_message_socket_sink_0 = lora.message_socket_sink() + self.lora_lora_receiver_0 = lora.lora_receiver(samp_rate, capture_freq, ([target_freq]), sf, internal_sampling_rate, 0.002) ################################################## # Connections ################################################## - self.connect((self.uhd_usrp_source_0, 0), (self.lora_lora_receiver_0, 0)) - self.connect((self.uhd_usrp_source_0, 0), (self.wxgui_fftsink2_1, 0)) - - def get_target_freq(self): - return self.target_freq - - def set_target_freq(self, target_freq): - self.target_freq = target_freq - self.set_offset(-(self.capture_freq - self.target_freq)) + self.msg_connect((self.lora_lora_receiver_0, 'frames'), (self.lora_message_socket_sink_0, 'in')) + self.connect((self.osmosdr_source_0, 0), (self.lora_lora_receiver_0, 0)) + self.connect((self.osmosdr_source_0, 0), (self.wxgui_fftsink2_1, 0)) def get_sf(self): return self.sf @@ -134,18 +105,9 @@ def get_samp_rate(self): def set_samp_rate(self, samp_rate): self.samp_rate = samp_rate self.wxgui_fftsink2_1.set_sample_rate(self.samp_rate) - self.uhd_usrp_source_0.set_samp_rate(self.samp_rate) + self.osmosdr_source_0.set_sample_rate(self.samp_rate) self.set_firdes_tap(firdes.low_pass(1, self.samp_rate, self.bw, 10000, firdes.WIN_HAMMING, 6.67)) - def get_capture_freq(self): - return self.capture_freq - - def set_capture_freq(self, capture_freq): - self.capture_freq = capture_freq - self.wxgui_fftsink2_1.set_baseband_freq(self.capture_freq) - self.uhd_usrp_source_0.set_center_freq(self.capture_freq, 0) - self.set_offset(-(self.capture_freq - self.target_freq)) - def get_bw(self): return self.bw @@ -155,17 +117,24 @@ def set_bw(self, bw): self.set_firdes_tap(firdes.low_pass(1, self.samp_rate, self.bw, 10000, firdes.WIN_HAMMING, 6.67)) self.set_bitrate(self.sf * (1 / (2**self.sf / self.bw))) + def get_target_freq(self): + return self.target_freq + + def set_target_freq(self, target_freq): + self.target_freq = target_freq + def get_symbols_per_sec(self): return self.symbols_per_sec def set_symbols_per_sec(self, symbols_per_sec): self.symbols_per_sec = symbols_per_sec - def get_offset(self): - return self.offset + def get_internal_sampling_rate(self): + return self.internal_sampling_rate - def set_offset(self, offset): - self.offset = offset + def set_internal_sampling_rate(self, internal_sampling_rate): + self.internal_sampling_rate = internal_sampling_rate + self.lora_lora_receiver_0.set_out_samp_rate(self.internal_sampling_rate) def get_firdes_tap(self): return self.firdes_tap @@ -173,13 +142,13 @@ def get_firdes_tap(self): def set_firdes_tap(self, firdes_tap): self.firdes_tap = firdes_tap - def get_finetune(self): - return self.finetune + def get_capture_freq(self): + return self.capture_freq - def set_finetune(self, finetune): - self.finetune = finetune - self._finetune_slider.set_value(self.finetune) - self._finetune_text_box.set_value(self.finetune) + def set_capture_freq(self, capture_freq): + self.capture_freq = capture_freq + self.wxgui_fftsink2_1.set_baseband_freq(self.capture_freq) + self.osmosdr_source_0.set_center_freq(self.capture_freq, 0) def get_bitrate(self): return self.bitrate diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..af52a8a --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,34 @@ +from archlinux:latest + +workdir /lib + +# Update to latest arch +run pacman -Syu --noconfirm + +# Install required dependencies +run pacman -S git python2-scipy swig cppunit fftw boost boost-libs gnuradio gnuradio-osmosdr libvolk log4cpp base-devel cmake wxgtk-common wxgtk2 wxgtk3 wxpython libuhd-firmware gnuradio-companion --noconfirm + +workdir /liquid + +# Manual liquid-dsp install +run git clone https://github.com/jgaeddert/liquid-dsp.git . && \ + sh ./bootstrap.sh && \ + sh ./configure --prefix=/usr && \ + make && \ + make install + +# Install gr-lora +workdir /src + +arg CACHEBUST +run git clone https://github.com/rpp0/gr-lora.git . && \ + mkdir build && \ + cd build && \ + cmake ../ -DCMAKE_INSTALL_PREFIX=/usr && \ + make && \ + make install && \ + ldconfig + +workdir /src/apps + +expose 40868 diff --git a/docker/docker_build_image.sh b/docker/docker_build_image.sh new file mode 100755 index 0000000..68efd47 --- /dev/null +++ b/docker/docker_build_image.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +LATEST_VERSION=`git ls-remote https://github.com/rpp0/gr-lora.git | grep HEAD | cut -f 1` +docker build -t rpp0/gr-lora --build-arg CACHEBUST=$LATEST_VERSION . +docker tag rpp0/gr-lora:latest rpp0/gr-lora:$LATEST_VERSION diff --git a/docker/docker_run_grlora.sh b/docker/docker_run_grlora.sh new file mode 100755 index 0000000..21706bd --- /dev/null +++ b/docker/docker_run_grlora.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +LATEST_VERSION=`git ls-remote https://github.com/rpp0/gr-lora.git | grep HEAD | cut -f 1` +DOCKER_XAUTH=/tmp/.docker.xauth +touch /tmp/.docker.xauth +xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $DOCKER_XAUTH nmerge - +docker run -i -t --rm --privileged -e DISPLAY=$DISPLAY -e XAUTHORITY=$DOCKER_XAUTH -v /tmp/.docker.xauth:/tmp/.docker.xauth:ro -v /tmp/.X11-unix:/tmp/.X11-unix:ro -v /dev/bus/usb:/dev/bus/usb --entrypoint /bin/bash rpp0/gr-lora:$LATEST_VERSION diff --git a/docker/mkimage-arch-pacman.conf b/docker/mkimage-arch-pacman.conf new file mode 100644 index 0000000..e6ef1e1 --- /dev/null +++ b/docker/mkimage-arch-pacman.conf @@ -0,0 +1,91 @@ +# +# /etc/pacman.conf +# +# See the pacman.conf(5) manpage for option and repository directives + +# +# GENERAL OPTIONS +# +[options] +# The following paths are commented out with their default values listed. +# If you wish to use different paths, uncomment and update the paths. +#RootDir = / +#DBPath = /var/lib/pacman/ +#CacheDir = /var/cache/pacman/pkg/ +#LogFile = /var/log/pacman.log +#GPGDir = /etc/pacman.d/gnupg/ +HoldPkg = pacman glibc +#XferCommand = /usr/bin/curl -C - -f %u > %o +#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u +#CleanMethod = KeepInstalled +#UseDelta = 0.7 +Architecture = auto + +# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup +#IgnorePkg = +#IgnoreGroup = + +#NoUpgrade = +#NoExtract = + +# Misc options +#UseSyslog +#Color +#TotalDownload +# We cannot check disk space from within a chroot environment +#CheckSpace +#VerbosePkgLists + +# By default, pacman accepts packages signed by keys that its local keyring +# trusts (see pacman-key and its man page), as well as unsigned packages. +SigLevel = Required DatabaseOptional +LocalFileSigLevel = Optional +#RemoteFileSigLevel = Required + +# NOTE: You must run `pacman-key --init` before first using pacman; the local +# keyring can then be populated with the keys of all official Arch Linux +# packagers with `pacman-key --populate archlinux`. + +# +# REPOSITORIES +# - can be defined here or included from another file +# - pacman will search repositories in the order defined here +# - local/custom mirrors can be added here or in separate files +# - repositories listed first will take precedence when packages +# have identical names, regardless of version number +# - URLs will have $repo replaced by the name of the current repo +# - URLs will have $arch replaced by the name of the architecture +# +# Repository entries are of the format: +# [repo-name] +# Server = ServerName +# Include = IncludePath +# +# The header [repo-name] is crucial - it must be present and +# uncommented to enable the repo. +# + +# The testing repositories are disabled by default. To enable, uncomment the +# repo name header and Include lines. You can add preferred servers immediately +# after the header, and they will be used before the default mirrors. + +#[testing] +#Include = /etc/pacman.d/mirrorlist + +[core] +Include = /etc/pacman.d/mirrorlist + +[extra] +Include = /etc/pacman.d/mirrorlist + +#[community-testing] +#Include = /etc/pacman.d/mirrorlist + +[community] +Include = /etc/pacman.d/mirrorlist + +# An example of a custom package repository. See the pacman manpage for +# tips on creating your own repositories. +#[custom] +#SigLevel = Optional TrustAll +#Server = file:///home/custompkgs diff --git a/docker/mkimage-arch.sh b/docker/mkimage-arch.sh new file mode 100755 index 0000000..ad63180 --- /dev/null +++ b/docker/mkimage-arch.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# Generate a minimal filesystem for archlinux and load it into the local +# docker as "archlinux" +# requires root +set -e + +hash pacstrap &>/dev/null || { + echo "Could not find pacstrap. Run pacman -S arch-install-scripts" + exit 1 +} + +hash expect &>/dev/null || { + echo "Could not find expect. Run pacman -S expect" + exit 1 +} + + +export LANG="C.UTF-8" + +ROOTFS=$(mktemp -d ${TMPDIR:-/var/tmp}/rootfs-archlinux-XXXXXXXXXX) +chmod 755 $ROOTFS + +# packages to ignore for space savings +PKGIGNORE=( + cryptsetup + device-mapper + dhcpcd + iproute2 + jfsutils + linux + lvm2 + man-db + man-pages + mdadm + nano + netctl + openresolv + pciutils + pcmciautils + reiserfsprogs + s-nail + systemd-sysvcompat + usbutils + vi + xfsprogs +) +IFS=',' +PKGIGNORE="${PKGIGNORE[*]}" +unset IFS + +arch="$(uname -m)" +# Set to arch="armv7hf" in order to cross-compile +case "$arch" in + armv*) + if pacman -Q archlinuxarm-keyring >/dev/null 2>&1; then + pacman-key --init + pacman-key --populate archlinuxarm + else + echo "Could not find archlinuxarm-keyring. Please, install it and run pacman-key --populate archlinuxarm" + exit 1 + fi + PACMAN_CONF=$(mktemp ${TMPDIR:-/var/tmp}/pacman-conf-archlinux-XXXXXXXXX) + version="$(echo $arch | cut -c 5)" + sed "s/Architecture = armv/Architecture = armv${version}h/g" './mkimage-archarm-pacman.conf' > "${PACMAN_CONF}" + PACMAN_MIRRORLIST='Server = http://mirror.archlinuxarm.org/$arch/$repo' + PACMAN_EXTRA_PKGS='archlinuxarm-keyring' + EXPECT_TIMEOUT=1800 # Most armv* based devices can be very slow (e.g. RPiv1) + ARCH_KEYRING=archlinuxarm + DOCKER_IMAGE_NAME="armv${version}h/archlinux" + ;; + *) + PACMAN_CONF='./mkimage-arch-pacman.conf' + PACMAN_MIRRORLIST='Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch' + PACMAN_EXTRA_PKGS='' + EXPECT_TIMEOUT=60 + ARCH_KEYRING=archlinux + DOCKER_IMAGE_NAME=archlinux + ;; +esac + +export PACMAN_MIRRORLIST + +expect < $ROOTFS/etc/locale.gen +arch-chroot $ROOTFS locale-gen +arch-chroot $ROOTFS /bin/sh -c 'echo $PACMAN_MIRRORLIST > /etc/pacman.d/mirrorlist' + +# udev doesn't work in containers, rebuild /dev +DEV=$ROOTFS/dev +rm -rf $DEV +mkdir -p $DEV +mknod -m 666 $DEV/null c 1 3 +mknod -m 666 $DEV/zero c 1 5 +mknod -m 666 $DEV/random c 1 8 +mknod -m 666 $DEV/urandom c 1 9 +mkdir -m 755 $DEV/pts +mkdir -m 1777 $DEV/shm +mknod -m 666 $DEV/tty c 5 0 +mknod -m 600 $DEV/console c 5 1 +mknod -m 666 $DEV/tty0 c 4 0 +mknod -m 666 $DEV/full c 1 7 +mknod -m 600 $DEV/initctl p +mknod -m 666 $DEV/ptmx c 5 2 +ln -sf /proc/self/fd $DEV/fd + +tar --numeric-owner --xattrs --acls -C $ROOTFS -c . | docker import - $DOCKER_IMAGE_NAME +docker run --rm -t $DOCKER_IMAGE_NAME echo Success. +rm -rf $ROOTFS diff --git a/examples/screenshot.png b/examples/screenshot.png index 7a8e6d1ced15d234889fd855187d85782cafd1f5..bbffecd754f08784ce25adfe26511705f42d6dee 100644 GIT binary patch literal 30378 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 literal 9589 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