Git Product home page Git Product logo

pyradiotracking's Introduction

pyradiotracking

Detect signals of wildlife tracking systems with RTL SDR devices.

Usage

$ python3 -m radiotracking -h
usage: radiotracking [-h] [-v] [--config CONFIG] [--station STATION] [--schedule [SCHEDULE [SCHEDULE ...]]] [-d [DEVICE [DEVICE ...]]]
                     [-c [CALIBRATION [CALIBRATION ...]]] [-f CENTER_FREQ] [-s SAMPLE_RATE] [-b SDR_CALLBACK_LENGTH] [-g GAIN]
                     [--sdr-max-restart SDR_MAX_RESTART] [--sdr-timeout-s SDR_TIMEOUT_S] [-n FFT_NPERSEG] [-w FFT_WINDOW] [-t SIGNAL_THRESHOLD_DBW]
                     [-r SNR_THRESHOLD_DB] [-l SIGNAL_MIN_DURATION_MS] [-u SIGNAL_MAX_DURATION_MS] [--matching-timeout-s MATCHING_TIMEOUT_S]
                     [-mt MATCHING_TIME_DIFF_S] [-mb MATCHING_BANDWIDTH_HZ] [-md MATCHING_DURATION_DIFF_MS] [--sig-stdout] [--match-stdout]
                     [--path PATH] [--csv] [--export-config] [--mqtt] [--mqtt-host MQTT_HOST] [--mqtt-port MQTT_PORT] [--dashboard]
                     [--dashboard-host DASHBOARD_HOST] [--dashboard-port DASHBOARD_PORT] [--dashboard-signals DASHBOARD_SIGNALS]

Detect signals of wildlife tracking systems with RTL SDR devices

optional arguments:
  -h, --help            show this help message and exit
  -v, --verbose         increase output verbosity (default: 0)
  --config CONFIG       configuration file (default: etc/radiotracking.ini)
  --station STATION     name of the station (default: test)
  --schedule [SCHEDULE [SCHEDULE ...]]
                        specify a schedule of operation, e.g. 18:00-18:59:59 (default: [])

rtl-sdr:
  -d [DEVICE [DEVICE ...]], --device [DEVICE [DEVICE ...]]
                        device indexes or names (default: ['0'])
  -c [CALIBRATION [CALIBRATION ...]], --calibration [CALIBRATION [CALIBRATION ...]]
                        device calibration gain (dB) (default: [])
  -f CENTER_FREQ, --center-freq CENTER_FREQ
                        center frequency to tune to (Hz) (default: 150150000)
  -s SAMPLE_RATE, --sample-rate SAMPLE_RATE
                        sample rate (Hz) (default: 300000)
  -b SDR_CALLBACK_LENGTH, --sdr-callback-length SDR_CALLBACK_LENGTH
                        number of samples to read per batch (default: None)
  -g GAIN, --gain GAIN  gain, supported levels 0.0 - 49.6 (default: 49.6)
  --sdr-max-restart SDR_MAX_RESTART
                        maximal restart count per SDR device (default: 3)
  --sdr-timeout-s SDR_TIMEOUT_S
                        Time after which an SDR device is considered unrepsonsive (s) (default: 2)

analysis:
  -n FFT_NPERSEG, --fft-nperseg FFT_NPERSEG
                        fft number of samples (default: 256)
  -w FFT_WINDOW, --fft-window FFT_WINDOW
                        fft window function (default: 'hamming')
  -t SIGNAL_THRESHOLD_DBW, --signal-threshold-dbw SIGNAL_THRESHOLD_DBW
                        lower limit for signal intensity (dBW) (default: -90.0)
  -r SNR_THRESHOLD_DB, --snr-threshold-db SNR_THRESHOLD_DB
                        lower limit for signal-to-noise ratio (dB) (default: 5.0)
  -l SIGNAL_MIN_DURATION_MS, --signal-min-duration-ms SIGNAL_MIN_DURATION_MS
                        lower limit for signal duration (ms) (default: 8)
  -u SIGNAL_MAX_DURATION_MS, --signal-max-duration-ms SIGNAL_MAX_DURATION_MS
                        upper limit for signal duration (ms) (default: 40)

matching:
  --matching-timeout-s MATCHING_TIMEOUT_S
                        timeout for adding signals to a match group (default: 2.0)
  -mt MATCHING_TIME_DIFF_S, --matching-time-diff-s MATCHING_TIME_DIFF_S
                        error margin for timestamp matching (s) (default: 0)
  -mb MATCHING_BANDWIDTH_HZ, --matching-bandwidth-hz MATCHING_BANDWIDTH_HZ
                        error margin for frequency (Hz) (default: 0)
  -md MATCHING_DURATION_DIFF_MS, --matching-duration-diff-ms MATCHING_DURATION_DIFF_MS
                        error margin for duration (ms) (default: None)

publish:
  --sig-stdout          enable stdout signal publishing (default: False)
  --match-stdout        enable stdout matched signals publishing (default: False)
  --path PATH           file output path (default: data)
  --csv                 enable csv data publishing (default: False)
  --export-config       export configuration (default: False)
  --mqtt                enable mqtt data publishing (default: False)
  --mqtt-host MQTT_HOST
                        hostname of mqtt broker (default: localhost)
  --mqtt-port MQTT_PORT
                        port of mqtt broker (default: 1883)

dashboard:
  --dashboard           enable web-dashboard (default: False)
  --dashboard-host DASHBOARD_HOST
                        hostname to bind the dashboard to (default: localhost)
  --dashboard-port DASHBOARD_PORT
                        port to bind the dashboard to (default: 8050)
  --dashboard-signals DASHBOARD_SIGNALS
                        number of signals to present (default: 100)

Troubleshooting

Failed to allocate zero-copy buffer

The size of zero-copy kernel buffers is limited.

Failed to allocate zero-copy buffer for transfer 5
Falling back to buffers in userspace
Failed to submit transfer 6
Please increase your allowed usbfs buffer size with the following command:
echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb

For larger buffers, as preferable for high sampling rates in terms of effificiency, the usbfs memory limit can be removed:

echo 0 | sudo tee /sys/module/usbcore/parameters/usbfs_memory_mb

Clock Drift, Resyncing

Each analyzer holds a time stamp internally to derive the time of a detected signal from. The clock is initialized on first async callback from the RTL-SDR library and then incremented according to the data retrieved from the SDR in the following callbacks. Whenever the calculated timestamp differs from the system clock by more than one block length this is detected:

SDR 0 total clock drift (1.1482 s) is larger than two blocks, signal detection is degraded. Resyncing...

Resyncing resets the internal clock to the current system clock, such that even though samples have been lost, the detected signals are using the correct timestamp.

Whenever resyncing is happening, there is a high likelihood, that signals detected in the previous blocks are also undergoing delay.

Resyncing can be prevented by using less ressource hungry settings, such as: higher signal / SNR thresholds, lower gain, lower sampling rate, larger callback length.

Caveats

pyradiotracking's People

Contributors

jonashoechst avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

trackit-systems

pyradiotracking's Issues

SDR process dying without recovery on start

May 09 02:50:29 xxx-002 bash[1268]: Found Rafael Micro R820T tuner
May 09 02:50:29 xxx-002 bash[1268]: [R82XX] PLL not locked!
May 09 02:50:29 xxx-002 bash[1268]: Allocating 15 zero-copy buffers
May 09 02:50:32 xxx-002 bash[1268]: Exception ignored on calling ctypes callback function: <bound method RtlSdr._bytes_converter_callback of <rtlsdr.rtlsdraio.RtlSdrAio object at 0x7fa7c4e610>>
May 09 02:50:32 xxx-002 bash[1268]: Traceback (most recent call last):
May 09 02:50:32 xxx-002 bash[1268]:   File "/usr/local/lib/python3.9/dist-packages/rtlsdr/rtlsdr.py", line 583, in _bytes_converter_callback
May 09 02:50:32 xxx-002 bash[1268]:     def _bytes_converter_callback(self, raw_buffer, num_bytes, context):
May 09 02:50:32 xxx-002 bash[1268]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 166, in handle_signal
May 09 02:50:32 xxx-002 bash[1268]:     logger.warning(f"SDR {self.device} received SIGALRM, last data received {datetime.datetime.now() - self._ts} ago.")
May 09 02:50:32 xxx-002 bash[1268]: TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'NoneType'

Add SNR signal filter

When extracting signals, not only the signal power, but also the signal-to-noise ratio could be useful to be applied as a filter. With high-gain / low signal threshold signals, thresholding SNR should further reduce false positives.

Implement binary search for power start / end search

The signal extraction on the spectrogram relies on a linear search.

# loop down until threshold is undershot
start = ti
start_min = 0 if self._spectrogram_last is None else -len(self._spectrogram_last[0])
while start > start_min:
if start < 0:
power = self._spectrogram_last[fi, start]
else:
power = fft[start]
if power < self.signal_threshold:
break
start -= 1
# loop up until threshold is undershot
end = ti
while end < len(fft):
if fft[end] < self.signal_threshold:
ti_skip = end
break
end += 1

When using binary search, a significant performance gain in low signal thresholds can be expected.

Bug: Crash during startup

python3 -m radiotracking -s 250000 -b 25000 -d 0 1 2 3

Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 612, in _bytes_converter_callback
    self._callback_bytes(values, context)
  File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 659, in _samples_converter_callback
    self._callback_samples(iq, context)
  File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 78, in process_samples
    signals = self.extract_signals(freqs, times, spectrogram, ts_start)
  File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 154, in extract_signals
    power = self._spectrogram_last[fi, start]
IndexError: index -5 is out of bounds for axis 1 with size 4

Show value of sliders in dashboard

There is a case for showing the value during a slider is moved in radiotracking's dashboard, i.e., when pinpointing a single sender when using the sliders.

Bug: Signal creation crashes in recent commit

In git version c81e16c, the signal creation crashes

Feb 14 13:23:32 umr-test-00001 bash[2371]: INFO:radiotracking.analyze:SDR 0 received data at 1644841412.248328
Feb 14 13:23:32 umr-test-00001 bash[2371]: Exception ignored on calling ctypes callback function: <bound method RtlSdr._bytes_converter_callback of <rtlsdr.rtlsdraio.RtlSdrAio object at 0x7f8ada9610>>
Feb 14 13:23:32 umr-test-00001 bash[2371]: Traceback (most recent call last):
Feb 14 13:23:32 umr-test-00001 bash[2371]:   File "/usr/local/lib/python3.9/dist-packages/rtlsdr/rtlsdr.py", line 612, in _bytes_converter_callback
Feb 14 13:23:32 umr-test-00001 bash[2371]:     self._callback_bytes(values, context)
Feb 14 13:23:32 umr-test-00001 bash[2371]:   File "/usr/local/lib/python3.9/dist-packages/rtlsdr/rtlsdr.py", line 659, in _samples_converter_callback
Feb 14 13:23:32 umr-test-00001 bash[2371]:     self._callback_samples(iq, context)
Feb 14 13:23:32 umr-test-00001 bash[2371]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 221, in process_samples
Feb 14 13:23:32 umr-test-00001 bash[2371]:     signals = self.extract_signals(freqs, times, spectrogram, ts_start)
Feb 14 13:23:32 umr-test-00001 bash[2371]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 425, in extract_signals
Feb 14 13:23:32 umr-test-00001 bash[2371]:     signal = Signal(self.device, ts.astimezone(pytz.utc), freq, duration, max_dBW, avg_dBW, std_dB, noise_dBW, snr_dB)
Feb 14 13:23:32 umr-test-00001 bash[2371]: TypeError: Can't instantiate abstract class Signal with abstract methods duration, frequency, ts

Separate Calibration from Operation mode

Calibration should not start normal operation, hence not export data (MQTT, CSV, ...). Calibration data should be persisted in distinguishable, hence diffenently named files.

Signal strength jumps sporadically in rare cases

In some cases we observe a plateau of increased signal strength of a single frequency / duration combination on a single receiver. The jump was observed in lab scenarios, as well as in the field with no users near the station.

2021-05-06 15 39 08

Race condition in dashboard signal deque

Mar 23 13:34:14 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:14] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:14 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:14] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:14 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:14] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:14 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:14] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:14 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:14] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:14 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:14] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:14 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:14] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:15 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:15] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:15 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:15] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:15 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:15] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:15 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:15] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:15 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:15] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:15 mof-rts-00011 bash[1708]: INFO:werkzeug:127.0.0.1 - - [23/Mar/2021 13:34:15] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 23 13:34:16 mof-rts-00011 bash[1708]: Exception on /radiotracking/_dash-update-component [POST]
Mar 23 13:34:16 mof-rts-00011 bash[1708]: Traceback (most recent call last):
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 2447, in wsgi_app
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     response = self.full_dispatch_request()
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1952, in full_dispatch_request
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     rv = self.handle_user_exception(e)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1821, in handle_user_exception
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     reraise(exc_type, exc_value, tb)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/_compat.py", line 39, in reraise
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     raise value
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1950, in full_dispatch_request
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     rv = self.dispatch_request()
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1936, in dispatch_request
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     return self.view_functions[rule.endpoint](**req.view_args)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/dash/dash.py", line 1078, in dispatch
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     response.set_data(func(*args, outputs_list=outputs_list))
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/dash/dash.py", line 1009, in add_context
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     output_value = func(*args, **kwargs)  # %% callback invoked %%
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/home/pi/pyradiotracking/radiotracking/present.py", line 448, in update_signal_variance
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     sigs = self.select_sigs(power, snr, freq, duration)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/home/pi/pyradiotracking/radiotracking/present.py", line 382, in select_sigs
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     return [sig for sig in self.signal_queue
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/home/pi/pyradiotracking/radiotracking/present.py", line 382, in <listcomp>
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     return [sig for sig in self.signal_queue
Mar 23 13:34:16 mof-rts-00011 bash[1708]: RuntimeError: deque mutated during iteration
Mar 23 13:34:16 mof-rts-00011 bash[1708]: ERROR:radiotracking.present:Exception on /radiotracking/_dash-update-component [POST]
Mar 23 13:34:16 mof-rts-00011 bash[1708]: Traceback (most recent call last):
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 2447, in wsgi_app
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     response = self.full_dispatch_request()
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1952, in full_dispatch_request
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     rv = self.handle_user_exception(e)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1821, in handle_user_exception
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     reraise(exc_type, exc_value, tb)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/_compat.py", line 39, in reraise
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     raise value
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1950, in full_dispatch_request
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     rv = self.dispatch_request()
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/flask/app.py", line 1936, in dispatch_request
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     return self.view_functions[rule.endpoint](**req.view_args)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/dash/dash.py", line 1078, in dispatch
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     response.set_data(func(*args, outputs_list=outputs_list))
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/usr/local/lib/python3.7/dist-packages/dash/dash.py", line 1009, in add_context
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     output_value = func(*args, **kwargs)  # %% callback invoked %%
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/home/pi/pyradiotracking/radiotracking/present.py", line 448, in update_signal_variance
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     sigs = self.select_sigs(power, snr, freq, duration)
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/home/pi/pyradiotracking/radiotracking/present.py", line 382, in select_sigs
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     return [sig for sig in self.signal_queue
Mar 23 13:34:16 mof-rts-00011 bash[1708]:   File "/home/pi/pyradiotracking/radiotracking/present.py", line 382, in <listcomp>
Mar 23 13:34:16 mof-rts-00011 bash[1708]:     return [sig for sig in self.signal_queue
Mar 23 13:34:16 mof-rts-00011 bash[1708]: RuntimeError: deque mutated during iteration

Move calibration to GUI

Calibration is an highly interactive process and thus needs to be a GUI function rather than a command line option.

Found new error message on mof-rts-00007 before receiver silently does not receive any signals.

the following message seems new to me and maybe it isn't handelt at the moment and can be a point to throw an eye on.

/usr/lib/python3/dist-packages/numpy/ctypeslib.py:527: RuntimeWarning: A builtin ctypes object gave a PEP3118 format string that does not match its itemsize, so a best-guess will be made of the data type. Newer versions of python may behave correctly.
Apr 05 12:16:17 mof-rts-00007 bash[13730]: Process SignalAnalyzer-1:
Apr 05 12:16:17 mof-rts-00007 bash[13730]: Traceback (most recent call last):
Apr 05 12:16:17 mof-rts-00007 bash[13730]:   File "/usr/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
Apr 05 12:16:17 mof-rts-00007 bash[13730]:     self.run()
Apr 05 12:16:17 mof-rts-00007 bash[13730]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 103, in run
Apr 05 12:16:17 mof-rts-00007 bash[13730]:     self.sdr.read_samples_async(self.process_samples, self.sdr_callback_length)
Apr 05 12:16:17 mof-rts-00007 bash[13730]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 632, in read_samples_async
Apr 05 12:16:17 mof-rts-00007 bash[13730]:     self.read_bytes_async(self._samples_converter_callback, num_bytes, context)
Apr 05 12:16:17 mof-rts-00007 bash[13730]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 577, in read_bytes_async
Apr 05 12:16:17 mof-rts-00007 bash[13730]:     raise LibUSBError(result, 'Could not read %d bytes' % (num_bytes))
Apr 05 12:16:17 mof-rts-00007 bash[13730]: rtlsdr.rtlsdr.LibUSBError: <LIBUSB_ERROR_NOT_FOUND (-5): Entity not found> "Could not read 600000 bytes"
Apr 05 12:16:17 mof-rts-00007 bash[13730]: Found Rafael Micro R820T tuner
Apr 05 12:16:18 mof-rts-00007 bash[13730]: [R82XX] PLL not locked!
Apr 05 12:16:18 mof-rts-00007 bash[13730]: Process SignalAnalyzer-2:
Apr 05 12:16:18 mof-rts-00007 bash[13730]: Traceback (most recent call last):
Apr 05 12:16:18 mof-rts-00007 bash[13730]:   File "/usr/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
Apr 05 12:16:18 mof-rts-00007 bash[13730]:     self.run()
Apr 05 12:16:18 mof-rts-00007 bash[13730]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 103, in run
Apr 05 12:16:18 mof-rts-00007 bash[13730]:     self.sdr.read_samples_async(self.process_samples, self.sdr_callback_length)
Apr 05 12:16:18 mof-rts-00007 bash[13730]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 632, in read_samples_async
Apr 05 12:16:18 mof-rts-00007 bash[13730]:     self.read_bytes_async(self._samples_converter_callback, num_bytes, context)
Apr 05 12:16:18 mof-rts-00007 bash[13730]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 577, in read_bytes_async
Apr 05 12:16:18 mof-rts-00007 bash[13730]:     raise LibUSBError(result, 'Could not read %d bytes' % (num_bytes))
Apr 05 12:16:18 mof-rts-00007 bash[13730]: rtlsdr.rtlsdr.LibUSBError: <LIBUSB_ERROR_NOT_FOUND (-5): Entity not found> "Could not read 600000 bytes"
Apr 05 12:16:18 mof-rts-00007 bash[13730]: Allocating 15 zero-copy buffers
Apr 05 12:16:18 mof-rts-00007 bash[13730]: Found Rafael Micro R820T tuner
Apr 05 12:16:19 mof-rts-00007 bash[13730]: [R82XX] PLL not locked!
Apr 05 12:16:19 mof-rts-00007 bash[13730]: Allocating 15 zero-copy buffers
Apr 05 12:16:34 mof-rts-00007 bash[13730]: /usr/lib/python3/dist-packages/numpy/ctypeslib.py:527: RuntimeWarning: A builtin ctypes object gave a PEP3118 format string that does not match its itemsize, so a best-guess will be made of the data type. Newer versions of python may behave correctly.
Apr 05 12:16:34 mof-rts-00007 bash[13730]:   return array(obj, copy=False)
Apr 05 12:16:34 mof-rts-00007 bash[13730]: WARNING:radiotracking.analyze:SDR 0 total clock drift (0.97416 s) is larger than two blocks, signal detection is degraded. Terminating...
Apr 05 12:16:35 mof-rts-00007 bash[13730]: Found Rafael Micro R820T tuner
Apr 05 12:16:36 mof-rts-00007 bash[13730]: [R82XX] PLL not locked!
Apr 05 12:16:36 mof-rts-00007 bash[13730]: Allocating 15 zero-copy buffers
Apr 05 16:18:27 mof-rts-00007 systemd[1]: Stopping RadioTracking...

Implement rudimentary scheduling of operation

The rudimentary scheduling should allow the operation to run for multiple defined intervals at the time of day. For all intervals the same config is applied.

Example: --schedule 04:00-09:30 18:00-23:00

feature request : deployment tool

In close accordance to GPS-device providers, it would be great to having a tool where each available (ordered) tag might be labeled with alias names. Could this be possible with a lookup table?
Hence, a given duration with a given frequency might carry and display the name
blackbird_1
greattit_9
myotis_2
...
And therefore accordingly filterable on the grafana dashboard.

Observed some error messages while digging into logs from mof-rts-00011

Mar 30 13:34:31 mof-rts-00011 bash[3799]: INFO:werkzeug:127.0.0.1 - - [30/Mar/2021 13:34:31] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 30 13:34:31 mof-rts-00011 bash[3799]: INFO:werkzeug:127.0.0.1 - - [30/Mar/2021 13:34:31] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 30 13:34:31 mof-rts-00011 bash[3799]: INFO:werkzeug:127.0.0.1 - - [30/Mar/2021 13:34:31] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 30 13:34:31 mof-rts-00011 bash[3799]: INFO:werkzeug:127.0.0.1 - - [30/Mar/2021 13:34:31] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 30 13:34:31 mof-rts-00011 bash[3799]: INFO:werkzeug:127.0.0.1 - - [30/Mar/2021 13:34:31] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 30 13:34:31 mof-rts-00011 bash[3799]: INFO:werkzeug:127.0.0.1 - - [30/Mar/2021 13:34:31] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 30 13:34:31 mof-rts-00011 bash[3799]: INFO:werkzeug:127.0.0.1 - - [30/Mar/2021 13:34:31] "POST /radiotracking/_dash-update-component HTTP/1.1" 200 -
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: 127.0.0.1 - - [30/Mar/2021 13:33:48] "GET / HTTP/1.1" 500 59
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: ----------------------------------------
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: Exception happened during processing of request from ('127.0.0.1', 57742)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: Traceback (most recent call last):
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 138, in run
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.finish_response()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 180, in finish_response
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.write(data)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 274, in write
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.send_headers()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 332, in send_headers
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.send_preamble()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 255, in send_preamble
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     ('Date: %s\r\n' % format_date_time(time.time())).encode('iso-8859-1')
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 453, in _write
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     result = self.stdout.write(data)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/socketserver.py", line 799, in write
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self._sock.sendall(b)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: BrokenPipeError: [Errno 32] Broken pipe
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: During handling of the above exception, another exception occurred:
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: Traceback (most recent call last):
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 141, in run
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.handle_error()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 368, in handle_error
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.finish_response()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 180, in finish_response
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.write(data)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 274, in write
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.send_headers()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 331, in send_headers
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     if not self.origin_server or self.client_is_modern():
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 344, in client_is_modern
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: TypeError: 'NoneType' object is not subscriptable
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: During handling of the above exception, another exception occurred:
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: Traceback (most recent call last):
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/socketserver.py", line 316, in _handle_request_noblock
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.process_request(request, client_address)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/socketserver.py", line 347, in process_request
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.finish_request(request, client_address)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/socketserver.py", line 360, in finish_request
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.RequestHandlerClass(request, client_address, self)
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/socketserver.py", line 720, in __init__
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.handle()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/simple_server.py", line 133, in handle
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     handler.run(self.server.get_app())
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 144, in run
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.close()
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:   File "/usr/lib/python3.7/wsgiref/simple_server.py", line 35, in close
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]:     self.status.split(' ',1)[0], self.bytes_sent
Mar 30 13:34:31 mof-rts-00011 sysdweb[789]: AttributeError: 'NoneType' object has no attribute 'split'

Observed some error messages while digging into logs from mof-rts-00011

Mar 23 13:39:09 mof-rts-00011 sysdweb[814]: 127.0.0.1 - - [23/Mar/2021 13:39:09] "GET /img/favicon.png HTTP/1.1" 200 8192
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]: Traceback (most recent call last):
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 138, in run
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:     self.finish_response()
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 180, in finish_response
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:     self.write(data)
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 274, in write
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:     self.send_headers()
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 332, in send_headers
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:     self.send_preamble()
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 255, in send_preamble
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:     ('Date: %s\r\n' % format_date_time(time.time())).encode('iso-8859-1')
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 453, in _write
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:     result = self.stdout.write(data)
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/socketserver.py", line 799, in write
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]:     self._sock.sendall(b)
Mar 23 13:39:09 mof-rts-00011 sysdweb[814]: BrokenPipeError: [Errno 32] Broken pipe

[Bug] Dead loggers do not lead to program termination when exceptions occur in the python callback

There seems to be a rare case, in which a dead logger leads to a process failing instead of restarting / terminating the program.

Mar 19 11:13:40 jonas-rpi-00001 python3[970]: INFO:radiotracking.analyze:SDR 0 recv 131072, clock drift: -0.021 s, filtered 0 / 0 signals, block len: 43.7 ms, compute: 20.8 ms
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: INFO:radiotracking.analyze:SDR 2 recv 131072, clock drift: -0.026 s, filtered 0 / 0 signals, block len: 43.7 ms, compute: 18.0 ms
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: INFO:radiotracking.analyze:SDR 1 recv 131072, clock drift: -0.024 s, filtered 0 / 0 signals, block len: 43.7 ms, compute: 21.5 ms
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: INFO:radiotracking.analyze:SDR 3 recv 131072, clock drift: -0.052 s, filtered 0 / 0 signals, block len: 43.7 ms, compute: 18.9 ms
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: /usr/lib/python3/dist-packages/numpy/ctypeslib.py:527: RuntimeWarning: A builtin ctypes object gave a PEP3118 format string that does not match its itemsize, so a best-guess will be made of the data type. Newer versions of python may behave correctly.
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   return array(obj, copy=False)
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: WARNING:radiotracking.analyze:SDR 3 total clock drift (0.14707 s) is larger than two blocks, signal detection is degraded. Resyncing...
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: /usr/lib/python3/dist-packages/scipy/signal/spectral.py:1773: UserWarning: nperseg = 256 is greater than input length  = 0, using nperseg = 0
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   .format(nperseg, input_length))
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: Traceback (most recent call last):
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "_ctypes/callbacks.c", line 232, in 'calling callback function'
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 612, in _bytes_converter_callback
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     self._callback_bytes(values, context)
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 659, in _samples_converter_callback
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     self._callback_samples(iq, context)
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 154, in process_samples
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     signals = self.extract_signals(freqs, times, spectrogram, ts_start)
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 227, in extract_signals
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     signal_min_duration_num = self.signal_min_duration / (times[1] - times[0])
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: IndexError: index 1 is out of bounds for axis 0 with size 0
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: WARNING:radiotracking.analyze:SDR 3 total clock drift (0.020442 s) is larger than two blocks, signal detection is degraded. Resyncing...
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: Traceback (most recent call last):
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "_ctypes/callbacks.c", line 232, in 'calling callback function'
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 612, in _bytes_converter_callback
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     self._callback_bytes(values, context)
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/usr/local/lib/python3.7/dist-packages/rtlsdr/rtlsdr.py", line 659, in _samples_converter_callback
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     self._callback_samples(iq, context)
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 154, in process_samples
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     signals = self.extract_signals(freqs, times, spectrogram, ts_start)
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:   File "/home/pi/pyradiotracking/radiotracking/analyze.py", line 227, in extract_signals
Mar 19 11:13:40 jonas-rpi-00001 python3[970]:     signal_min_duration_num = self.signal_min_duration / (times[1] - times[0])
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: IndexError: index 1 is out of bounds for axis 0 with size 0
Mar 19 11:13:40 jonas-rpi-00001 python3[970]: WARNING:radiotracking.analyze:SDR 3 total clock drift (0.008299 s) is larger than two blocks, signal detection is degraded. Resyncing...

Keepalive for SDR Sticks

Requested is a simple CSV file, which writes in a given periodical time delay information on the successful running of an SDR Stick, e.g.

Time; SDR; running
2022-05-15T09:32:01; 1; running

Observed some error messages while digging into logs from mof-rts-00011

Aug 20 13:57:07 mof-rts-00011 dhclient[349]: No DHCPOFFERS received.
Aug 20 13:57:07 mof-rts-00011 dhclient[349]: No working leases in persistent database - sleeping.
Aug 20 13:57:07 mof-rts-00011 sh[326]: No DHCPOFFERS received.
Aug 20 13:57:07 mof-rts-00011 sh[326]: No working leases in persistent database - sleeping.
Aug 20 13:57:07 mof-rts-00011 sh[326]: eth0=eth0
Aug 20 13:57:08 mof-rts-00011 sh[1113]: eth1=eth1
Aug 20 13:57:10 mof-rts-00011 dhcpcd[651]: eth1: no IPv6 Routers available
Aug 20 13:57:11 mof-rts-00011 hostapd[818]: wlan0: STA 9c:29:76:cd:91:28 IEEE 802.11: associated
Aug 20 13:57:11 mof-rts-00011 hostapd[818]: wlan0: STA 9c:29:76:cd:91:28 RADIUS: starting accounting session BD5BDA784FF88F99
Aug 20 13:57:11 mof-rts-00011 hostapd[818]: wlan0: STA 9c:29:76:cd:91:28 WPA: pairwise key handshake completed (RSN)
Aug 20 13:57:14 mof-rts-00011 dnsmasq-dhcp[826]: DHCPDISCOVER(wlan0) 9c:29:76:cd:91:28
Aug 20 13:57:14 mof-rts-00011 dnsmasq-dhcp[826]: DHCPOFFER(wlan0) 169.254.0.12 9c:29:76:cd:91:28
Aug 20 13:57:14 mof-rts-00011 dnsmasq-dhcp[826]: DHCPREQUEST(wlan0) 169.254.0.12 9c:29:76:cd:91:28
Aug 20 13:57:14 mof-rts-00011 dnsmasq-dhcp[826]: Ignoring domain AD.Uni-Marburg.DE for DHCP host name pc19676
Aug 20 13:57:14 mof-rts-00011 dnsmasq-dhcp[826]: DHCPACK(wlan0) 169.254.0.12 9c:29:76:cd:91:28 pc19676
Aug 20 13:57:14 mof-rts-00011 dnsmasq[826]: nameserver 192.168.8.1 refused to do a recursive query
Aug 20 13:57:14 mof-rts-00011 dnsmasq[826]: nameserver 8.8.4.4 refused to do a recursive query
Aug 20 13:57:15 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924635.2636697,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62798","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Content-Type":["application/json"],"Content-Length":["434"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"X-Csrftoken":["undefined"]}},"duration":0.010054218,"status":502,"err_id":"g0z8wvc9r","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:15 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924635.2637136,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62796","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Referer":["http://169.254.0.1/radiotracking/"],"Origin":["http://169.254.0.1"],"Content-Length":["204"],"Connection":["keep-alive"],"Accept":["application/json"],"Accept-Encoding":["gzip, deflate"],"Content-Type":["application/json"],"X-Csrftoken":["undefined"]}},"duration":0.015555,"status":502,"err_id":"kcck5v2au","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:15 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924635.2662754,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62802","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Referer":["http://169.254.0.1/radiotracking/"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"Content-Type":["application/json"],"X-Csrftoken":["undefined"],"Content-Length":["200"]}},"duration":0.005699583,"status":502,"err_id":"r04f1v912","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:15 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924635.279178,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62803","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"X-Csrftoken":["undefined"],"Origin":["http://169.254.0.1"],"Content-Length":["442"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept":["application/json"],"Content-Type":["application/json"],"Connection":["keep-alive"]}},"duration":0.006838021,"status":502,"err_id":"se5cc650h","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:15 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924635.2815754,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62804","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"X-Csrftoken":["undefined"],"Content-Length":["188"],"Accept":["application/json"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"]}},"duration":0.005973698,"status":502,"err_id":"wqatyyakf","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:15 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924635.282522,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62798","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"Origin":["http://169.254.0.1"],"Content-Length":["436"],"Connection":["keep-alive"],"Accept":["application/json"],"Content-Type":["application/json"],"X-Csrftoken":["undefined"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"]}},"duration":0.005086823,"status":502,"err_id":"m1nnzb934","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:15 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924635.2943897,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62807","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Accept":["application/json"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"X-Csrftoken":["undefined"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Content-Type":["application/json"],"Origin":["http://169.254.0.1"],"Content-Length":["450"],"Connection":["keep-alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"]}},"duration":0.006640365,"status":502,"err_id":"eh9957f8c","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:17 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924637.8942258,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62802","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Content-Type":["application/json"],"X-Csrftoken":["undefined"],"Connection":["keep-alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept":["application/json"],"Referer":["http://169.254.0.1/radiotracking/"],"Origin":["http://169.254.0.1"],"Content-Length":["204"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"]}},"duration":0.004448021,"status":502,"err_id":"r30380hdm","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:17 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924637.897496,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62796","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"],"Accept":["application/json"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"X-Csrftoken":["undefined"],"Content-Length":["434"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Encoding":["gzip, deflate"]}},"duration":0.005682969,"status":502,"err_id":"n8qq86m2s","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:17 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924637.9027212,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62803","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"Origin":["http://169.254.0.1"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Content-Type":["application/json"],"X-Csrftoken":["undefined"],"Content-Length":["200"],"Connection":["keep-alive"]}},"duration":0.007184062,"status":502,"err_id":"2zt1zum85","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:17 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924637.9043725,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62804","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Content-Type":["application/json"],"Connection":["keep-alive"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"X-Csrftoken":["undefined"],"Origin":["http://169.254.0.1"],"Content-Length":["442"]}},"duration":0.004029896,"status":502,"err_id":"t9vuebzus","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:17 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924637.9056916,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62798","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Origin":["http://169.254.0.1"],"Content-Length":["188"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept":["application/json"],"Content-Type":["application/json"],"Referer":["http://169.254.0.1/radiotracking/"],"X-Csrftoken":["undefined"],"Connection":["keep-alive"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"]}},"duration":0.004200834,"status":502,"err_id":"cz7f5y089","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:17 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924637.9086633,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62807","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Origin":["http://169.254.0.1"],"Content-Length":["450"],"Connection":["keep-alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"Accept":["application/json"],"Accept-Encoding":["gzip, deflate"],"X-Csrftoken":["undefined"]}},"duration":0.006240416,"status":502,"err_id":"cdjacbtvp","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:17 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924637.9111888,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62802","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"X-Csrftoken":["undefined"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"],"Accept":["application/json"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"Content-Length":["436"]}},"duration":0.003893646,"status":502,"err_id":"z2gx9kuf9","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:19 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924639.9418905,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62796","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Connection":["keep-alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"X-Csrftoken":["undefined"],"Origin":["http://169.254.0.1"],"Content-Length":["204"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"]}},"duration":0.004182708,"status":502,"err_id":"i0k3xwiwp","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:20 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924639.9508944,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62803","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Content-Type":["application/json"],"Origin":["http://169.254.0.1"],"Content-Length":["434"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"X-Csrftoken":["undefined"],"Connection":["keep-alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"]}},"duration":0.004462084,"status":502,"err_id":"ssashsb5c","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:20 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924639.9554782,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62798","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"X-Csrftoken":["undefined"],"Origin":["http://169.254.0.1"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"Content-Length":["442"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Connection":["keep-alive"]}},"duration":0.005037709,"status":502,"err_id":"xiui81fnw","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:20 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924639.9588315,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62804","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Accept-Encoding":["gzip, deflate"],"X-Csrftoken":["undefined"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"Content-Length":["200"]}},"duration":0.008972708,"status":502,"err_id":"9wriqcwpn","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:20 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924639.9642131,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62807","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"X-Csrftoken":["undefined"],"Content-Length":["188"],"Connection":["keep-alive"],"Origin":["http://169.254.0.1"],"Accept":["application/json"]}},"duration":0.00692875,"status":502,"err_id":"ct7qkd3ku","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:20 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924639.9649112,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62802","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"Referer":["http://169.254.0.1/radiotracking/"],"X-Csrftoken":["undefined"],"Content-Length":["450"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"Content-Type":["application/json"]}},"duration":0.005677968,"status":502,"err_id":"rbb7jjsvj","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:20 mof-rts-00011 caddy[808]: {"level":"error","ts":1597924639.9967556,"logger":"http.log.error","msg":"dial tcp [::1]:8050: connect: connection refused","request":{"remote_addr":"169.254.0.12:62796","proto":"HTTP/1.1","method":"POST","host":"169.254.0.1","uri":"/radiotracking/_dash-update-component","headers":{"Content-Length":["436"],"Accept":["application/json"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate"],"Referer":["http://169.254.0.1/radiotracking/"],"Content-Type":["application/json"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0"],"X-Csrftoken":["undefined"],"Origin":["http://169.254.0.1"],"Connection":["keep-alive"]}},"duration":0.003625625,"status":502,"err_id":"1hrbep7cv","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]: Bottle v0.12.19 server starting up (using WSGIRefServer())...
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]: Listening on http://0.0.0.0:10080/
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]: Hit Ctrl-C to quit.
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]: 127.0.0.1 - - [20/Aug/2020 13:57:21] "GET / HTTP/1.1" 200 11764
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]: 127.0.0.1 - - [20/Aug/2020 13:57:21] "GET /img/favicon.png HTTP/1.1" 200 8192
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]: Traceback (most recent call last):
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 138, in run
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:     self.finish_response()
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 180, in finish_response
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:     self.write(data)
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 274, in write
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:     self.send_headers()
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 332, in send_headers
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:     self.send_preamble()
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 255, in send_preamble
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:     ('Date: %s\r\n' % format_date_time(time.time())).encode('iso-8859-1')
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/wsgiref/handlers.py", line 453, in _write
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:     result = self.stdout.write(data)
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:   File "/usr/lib/python3.7/socketserver.py", line 799, in write
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]:     self._sock.sendall(b)
Aug 20 13:57:21 mof-rts-00011 sysdweb[814]: BrokenPipeError: [Errno 32] Broken pipe
Aug 20 13:57:23 mof-rts-00011 systemd[1]: dev-sda1.device: Job dev-sda1.device/start timed out.
Aug 20 13:57:23 mof-rts-00011 systemd[1]: Timed out waiting for device /dev/sda1.
Aug 20 13:57:23 mof-rts-00011 systemd[1]: Dependency failed for /data.

Implement watchdog functionality

When an RtlSdr stick fails, there is no message handed to the respective analysis thread by the pyrtlsdr library. To recognize the error, a watchdog should be added, which checks if data has been collected from the device in the last interval.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.