mirror of
https://github.com/30hours/blah2.git
synced 2024-11-18 12:33:58 +00:00
Making some changes v1
This commit is contained in:
commit
3738f78eee
29 changed files with 2506 additions and 1789 deletions
|
@ -3,39 +3,40 @@
|
|||
# ubuntu-22.04 by default
|
||||
ARG VARIANT="jammy"
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/cpp:0-${VARIANT}
|
||||
LABEL maintainer="30hours <nathan@30hours.dev>"
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
# Feel like this shouldn't be needed but it drops me in / during build
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN apt-get update \
|
||||
#
|
||||
# Install dev tools and package dependencies
|
||||
&& apt-get install -y clang-tidy clang-format doxygen graphviz gfortran \
|
||||
libfftw3-dev liblapack-dev libopenblas-dev libudev-dev libusb-1.0.0-dev \
|
||||
#
|
||||
# Clean up
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /blah2
|
||||
ADD lib lib
|
||||
RUN apt-get update && apt-get install -y software-properties-common \
|
||||
&& apt-add-repository ppa:ettusresearch/uhd \
|
||||
&& apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install -y \
|
||||
g++ make cmake git curl zip unzip doxygen graphviz \
|
||||
libfftw3-dev pkg-config gfortran \
|
||||
libuhd-dev=4.6.0.0-0ubuntu1~jammy1 \
|
||||
uhd-host=4.6.0.0-0ubuntu1~jammy1 \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install dependencies from vcpkg
|
||||
RUN vcpkg integrate install \
|
||||
&& vcpkg install catch2 \
|
||||
&& vcpkg install rapidjson \
|
||||
&& vcpkg install asio \
|
||||
&& vcpkg install cpp-httplib \
|
||||
&& vcpkg install armadillo \
|
||||
&& vcpkg install ryml
|
||||
# install dependencies from vcpkg
|
||||
RUN git clone https://github.com/microsoft/vcpkg /opt/vcpkg \
|
||||
&& /opt/vcpkg/bootstrap-vcpkg.sh
|
||||
ENV PATH="/opt/vcpkg:${PATH}" VCPKG_ROOT=/opt/vcpkg
|
||||
RUN cd /blah2/lib && vcpkg integrate install \
|
||||
&& vcpkg install --clean-after-build
|
||||
|
||||
COPY lib/sdrplay-3.0.7/SDRplay_RSP_API-Linux-3.07.1.run /workspace/
|
||||
# install SDRplay API
|
||||
RUN chmod +x /blah2/lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run \
|
||||
&& /blah2/lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run --tar -xvf -C /blah2/lib/sdrplay-3.14.0 \
|
||||
&& cp /blah2/lib/sdrplay-3.14.0/x86_64/libsdrplay_api.so.3.14 /usr/local/lib/libsdrplay_api.so \
|
||||
&& cp /blah2/lib/sdrplay-3.14.0/x86_64/libsdrplay_api.so.3.14 /usr/local/lib/libsdrplay_api.so.3.14 \
|
||||
&& cp /blah2/lib/sdrplay-3.14.0/inc/* /usr/local/include \
|
||||
&& chmod 644 /usr/local/lib/libsdrplay_api.so /usr/local/lib/libsdrplay_api.so.3.14 \
|
||||
&& ldconfig
|
||||
|
||||
# Install shitty sdrplay API
|
||||
RUN chmod +x /workspace/SDRplay_RSP_API-Linux-3.07.1.run \
|
||||
&& /workspace/SDRplay_RSP_API-Linux-3.07.1.run --tar -xf \
|
||||
&& cp x86_64/libsdrplay_api.so.3.07 /usr/local/lib/libsdrplay_api.so \
|
||||
&& ln -s /usr/local/lib/libsdrplay_api.so /usr/local/lib/libsdrplay_api.so.3.07 \
|
||||
&& cp inc/* /usr/local/include \
|
||||
&& chmod 644 /usr/local/lib/libsdrplay_api.so /usr/local/lib/libsdrplay_api.so.3.07 \
|
||||
&& ldconfig
|
||||
# install UHD API
|
||||
RUN uhd_images_downloader
|
||||
|
||||
|
|
|
@ -1 +1,23 @@
|
|||
# todo: write readme on using devcontainer with dev-release
|
||||
# todo: currently not working
|
||||
|
||||
## Usage
|
||||
|
||||
Install a recent `nodejs` using [nvm](https://github.com/nvm-sh/nvm).
|
||||
|
||||
```
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
||||
nvm install node 21.6.2
|
||||
```
|
||||
|
||||
Install the latest [devcontainer CLI](https://code.visualstudio.com/docs/devcontainers/devcontainer-cli).
|
||||
|
||||
```
|
||||
npm install -g @devcontainers/cli
|
||||
devcontainer --version
|
||||
```
|
||||
|
||||
Run the devcontainer.
|
||||
|
||||
```
|
||||
devcontainer up --workspace-folder .
|
||||
```
|
|
@ -1,5 +1,7 @@
|
|||
version: "3.2"
|
||||
|
||||
services:
|
||||
|
||||
blah2-dev:
|
||||
user: vscode
|
||||
build:
|
||||
|
|
|
@ -35,7 +35,7 @@ include_directories(src ${UHD_INCLUDE_DIRS})
|
|||
# TODO: create FindSdrplay.cmake for this
|
||||
add_library(sdrplay /usr/local/include/sdrplay_api.h)
|
||||
set_target_properties(sdrplay PROPERTIES LINKER_LANGUAGE C)
|
||||
target_link_libraries(sdrplay PUBLIC /usr/local/lib/libsdrplay_api.so.3.12)
|
||||
target_link_libraries(sdrplay PUBLIC /usr/local/lib/libsdrplay_api.so.3.14)
|
||||
|
||||
add_executable(blah2
|
||||
src/blah2.cpp
|
||||
|
|
13
Dockerfile
13
Dockerfile
|
@ -1,5 +1,6 @@
|
|||
FROM ubuntu:22.04 as blah2_env
|
||||
LABEL maintainer="30hours <nathan@30hours.dev>"
|
||||
LABEL org.opencontainers.image.source https://github.com/30hours/blah2
|
||||
|
||||
WORKDIR /blah2
|
||||
ADD lib lib
|
||||
|
@ -23,12 +24,12 @@ RUN cd /blah2/lib && vcpkg integrate install \
|
|||
&& vcpkg install --clean-after-build
|
||||
|
||||
# install SDRplay API
|
||||
RUN chmod +x /blah2/lib/sdrplay-3.12.1/SDRplay_RSP_API-Linux-3.12.1.run \
|
||||
&& /blah2/lib/sdrplay-3.12.1/SDRplay_RSP_API-Linux-3.12.1.run --tar -xvf -C /blah2/lib/sdrplay-3.12.1 \
|
||||
&& cp /blah2/lib/sdrplay-3.12.1/x86_64/libsdrplay_api.so.3.12 /usr/local/lib/libsdrplay_api.so \
|
||||
&& cp /blah2/lib/sdrplay-3.12.1/x86_64/libsdrplay_api.so.3.12 /usr/local/lib/libsdrplay_api.so.3.12 \
|
||||
&& cp /blah2/lib/sdrplay-3.12.1/inc/* /usr/local/include \
|
||||
&& chmod 644 /usr/local/lib/libsdrplay_api.so /usr/local/lib/libsdrplay_api.so.3.12 \
|
||||
RUN chmod +x /blah2/lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run \
|
||||
&& /blah2/lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run --tar -xvf -C /blah2/lib/sdrplay-3.14.0 \
|
||||
&& cp /blah2/lib/sdrplay-3.14.0/x86_64/libsdrplay_api.so.3.14 /usr/local/lib/libsdrplay_api.so \
|
||||
&& cp /blah2/lib/sdrplay-3.14.0/x86_64/libsdrplay_api.so.3.14 /usr/local/lib/libsdrplay_api.so.3.14 \
|
||||
&& cp /blah2/lib/sdrplay-3.14.0/inc/* /usr/local/include \
|
||||
&& chmod 644 /usr/local/lib/libsdrplay_api.so /usr/local/lib/libsdrplay_api.so.3.14 \
|
||||
&& ldconfig
|
||||
|
||||
# install UHD API
|
||||
|
|
40
Jenkinsfile
vendored
Normal file
40
Jenkinsfile
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
pipeline {
|
||||
agent any
|
||||
|
||||
environment {
|
||||
GHCR_REGISTRY = "ghcr.io"
|
||||
GHCR_TOKEN = credentials('ghcr-login')
|
||||
BLAH2_NAME = "30hours/blah2"
|
||||
BLAH2_API_NAME = "30hours/blah2_api"
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
echo 'Building the project'
|
||||
sh 'docker build -t $BLAH2_NAME .'
|
||||
sh 'docker build -t $BLAH2_API_NAME --file ./api/Dockerfile ./api'
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
steps {
|
||||
echo 'Running tests'
|
||||
}
|
||||
}
|
||||
stage('Push') {
|
||||
steps {
|
||||
sh 'echo $GHCR_TOKEN_PSW | docker login ghcr.io -u $GHCR_TOKEN_USR --password-stdin'
|
||||
sh 'docker tag $BLAH2_NAME ghcr.io/$BLAH2_NAME'
|
||||
sh 'docker tag $BLAH2_API_NAME ghcr.io/$BLAH2_API_NAME'
|
||||
sh 'docker push ghcr.io/$BLAH2_NAME'
|
||||
sh 'docker push ghcr.io/$BLAH2_API_NAME'
|
||||
sh 'docker logout'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,8 +33,8 @@ The build environment consists of a docker-compose.yml file running the followin
|
|||
sudo git clone http://github.com/30hours/blah2 /opt/blah2
|
||||
cd /opt/blah2
|
||||
vim config/config.yml
|
||||
./lib/sdrplay-3.12.1/SDRplay_RSP_API-Linux-3.12.1.run --tar -xvf -C ./lib/sdrplay-3.12.1
|
||||
./lib/sdrplay-3.12.1/install_lib.sh
|
||||
./lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run --tar -xvf -C ./lib/sdrplay-3.14.0
|
||||
./lib/sdrplay-3.14.0/install_lib.sh
|
||||
sudo docker network create blah2
|
||||
sudo systemctl enable docker
|
||||
sudo docker compose up -d
|
||||
|
@ -61,6 +61,8 @@ The radar processing output is available on [http://localhost:49152](http://loca
|
|||
|
||||
## Contributing
|
||||
|
||||
Join the [Discord](https://discord.gg/ewNQbeK5Zn) chat for sharing results and support.
|
||||
|
||||
Pull requests are welcome - especially for adding support for a new SDR.
|
||||
|
||||
- Currently have an issue where the USRP B210 is timing out after 5-10 mins and crashes the code. Convinced it's an issue with my usage of the API - email me for more info.
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
FROM node:16
|
||||
|
||||
LABEL maintainer="30hours <nathan@30hours.dev>"
|
||||
LABEL org.opencontainers.image.source https://github.com/30hours/blah2
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.16.1"
|
||||
"express": "^4.16.1",
|
||||
"js-yaml": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
|
129
api/server.js
129
api/server.js
|
@ -1,6 +1,17 @@
|
|||
const express = require('express');
|
||||
const dgram = require('dgram');
|
||||
const net = require("net");
|
||||
const fs = require('fs');
|
||||
const yaml = require('js-yaml');
|
||||
const dns = require('dns');
|
||||
|
||||
// parse config file
|
||||
var config;
|
||||
try {
|
||||
const file = process.argv[2];
|
||||
config = yaml.load(fs.readFileSync(file, 'utf8'));
|
||||
} catch (e) {
|
||||
console.error('Error reading or parsing the YAML file:', e);
|
||||
}
|
||||
|
||||
var stash_map = require('./stash/maxhold.js');
|
||||
var stash_detection = require('./stash/detection.js');
|
||||
|
@ -9,8 +20,8 @@ var stash_timing = require('./stash/timing.js');
|
|||
var stash_falsetargets = require('./stash/falsetargets.js');
|
||||
|
||||
// constants
|
||||
const PORT = 3000;
|
||||
const HOST = '0.0.0.0';
|
||||
const PORT = config.network.ports.api;
|
||||
const HOST = config.network.ip;
|
||||
var map = '';
|
||||
var detection = '';
|
||||
var track = '';
|
||||
|
@ -18,7 +29,6 @@ var timestamp = '';
|
|||
var timing = '';
|
||||
var iqdata = '';
|
||||
var falsetargets = '';
|
||||
var data = '';
|
||||
var data_map;
|
||||
var data_detection;
|
||||
var data_tracker;
|
||||
|
@ -59,9 +69,34 @@ app.get('/api/timing', (req, res) => {
|
|||
app.get('/api/iqdata', (req, res) => {
|
||||
res.send(iqdata);
|
||||
});
|
||||
app.get('/api/adsb2dd', (req, res) => {
|
||||
if (config.truth.adsb.enabled == true) {
|
||||
const api_url = "https://adsb2dd.30hours.dev/api/dd";
|
||||
const api_query =
|
||||
api_url +
|
||||
"?rx=" + config.location.rx.latitude + "," +
|
||||
config.location.rx.longitude + "," +
|
||||
config.location.rx.altitude +
|
||||
"&tx=" + config.location.tx.latitude + "," +
|
||||
config.location.tx.longitude + "," +
|
||||
config.location.tx.altitude +
|
||||
"&fc=" + (config.capture.fc / 1000000) +
|
||||
"&server=" + "http://" + config.truth.adsb.ip;
|
||||
const jsonResponse = {
|
||||
url: api_query
|
||||
};
|
||||
res.json(jsonResponse);
|
||||
}
|
||||
else {
|
||||
res.status(400).end();
|
||||
}
|
||||
});
|
||||
app.get('/api/falsetargets', (req, res) => {
|
||||
res.send(falsetargets);
|
||||
});
|
||||
app.get('/api/config', (req, res) => {
|
||||
res.send(config);
|
||||
});
|
||||
|
||||
// stash API
|
||||
app.get('/stash/map', (req, res) => {
|
||||
|
@ -94,57 +129,56 @@ app.listen(PORT, HOST, () => {
|
|||
});
|
||||
|
||||
// tcp listener map
|
||||
const server_map = net.createServer((socket) => {
|
||||
socket.write("Hello From Server!")
|
||||
socket.on("data", (msg) => {
|
||||
data_map = data_map + msg.toString();
|
||||
if (data_map.slice(-1) === "}") {
|
||||
map = data_map;
|
||||
data_map = '';
|
||||
}
|
||||
});
|
||||
socket.on("close", () => {
|
||||
console.log("Connection closed.");
|
||||
})
|
||||
const server_map = net.createServer((socket)=>{
|
||||
socket.on("data",(msg)=>{
|
||||
data_map = data_map + msg.toString();
|
||||
if (data_map.slice(-1) === "}")
|
||||
{
|
||||
map = data_map;
|
||||
data_map = '';
|
||||
}
|
||||
});
|
||||
socket.on("close",()=>{
|
||||
console.log("Connection closed.");
|
||||
})
|
||||
});
|
||||
server_map.listen(3001);
|
||||
server_map.listen(config.network.ports.map);
|
||||
|
||||
// tcp listener detection
|
||||
const server_detection = net.createServer((socket) => {
|
||||
socket.write("Hello From Server!")
|
||||
socket.on("data", (msg) => {
|
||||
data_detection = data_detection + msg.toString();
|
||||
if (data_detection.slice(-1) === "}") {
|
||||
detection = data_detection;
|
||||
data_detection = '';
|
||||
}
|
||||
const server_detection = net.createServer((socket)=>{
|
||||
socket.on("data",(msg)=>{
|
||||
data_detection = data_detection + msg.toString();
|
||||
if (data_detection.slice(-1) === "}")
|
||||
{
|
||||
detection = data_detection;
|
||||
data_detection = '';
|
||||
}
|
||||
});
|
||||
socket.on("close", () => {
|
||||
console.log("Connection closed.");
|
||||
})
|
||||
});
|
||||
server_detection.listen(3002);
|
||||
server_detection.listen(config.network.ports.detection);
|
||||
|
||||
// tcp listener tracker
|
||||
const server_tracker = net.createServer((socket) => {
|
||||
socket.write("Hello From Server!")
|
||||
socket.on("data", (msg) => {
|
||||
data_tracker = data_tracker + msg.toString();
|
||||
if (data_tracker.slice(-1) === "}") {
|
||||
track = data_tracker;
|
||||
data_tracker = '';
|
||||
}
|
||||
const server_tracker = net.createServer((socket)=>{
|
||||
socket.on("data",(msg)=>{
|
||||
data_tracker = data_tracker + msg.toString();
|
||||
if (data_tracker.slice(-1) === "}")
|
||||
{
|
||||
track = data_tracker;
|
||||
data_tracker = '';
|
||||
}
|
||||
});
|
||||
socket.on("close", () => {
|
||||
console.log("Connection closed.");
|
||||
})
|
||||
});
|
||||
server_tracker.listen(3003);
|
||||
server_tracker.listen(config.network.ports.track);
|
||||
|
||||
// tcp listener timestamp
|
||||
const server_timestamp = net.createServer((socket) => {
|
||||
socket.write("Hello From Server!")
|
||||
socket.on("data", (msg) => {
|
||||
const server_timestamp = net.createServer((socket)=>{
|
||||
socket.on("data",(msg)=>{
|
||||
data_timestamp = data_timestamp + msg.toString();
|
||||
timestamp = data_timestamp;
|
||||
data_timestamp = '';
|
||||
|
@ -153,12 +187,11 @@ const server_timestamp = net.createServer((socket) => {
|
|||
console.log("Connection closed.");
|
||||
})
|
||||
});
|
||||
server_timestamp.listen(4000);
|
||||
server_timestamp.listen(config.network.ports.timestamp);
|
||||
|
||||
// tcp listener timing
|
||||
const server_timing = net.createServer((socket) => {
|
||||
socket.write("Hello From Server!")
|
||||
socket.on("data", (msg) => {
|
||||
const server_timing = net.createServer((socket)=>{
|
||||
socket.on("data",(msg)=>{
|
||||
data_timing = data_timing + msg.toString();
|
||||
if (data_timing.slice(-1) === "}") {
|
||||
timing = data_timing;
|
||||
|
@ -169,12 +202,11 @@ const server_timing = net.createServer((socket) => {
|
|||
console.log("Connection closed.");
|
||||
})
|
||||
});
|
||||
server_timing.listen(4001);
|
||||
server_timing.listen(config.network.ports.timing);
|
||||
|
||||
// tcp listener iqdata metadata
|
||||
const server_iqdata = net.createServer((socket) => {
|
||||
socket.write("Hello From Server!")
|
||||
socket.on("data", (msg) => {
|
||||
const server_iqdata = net.createServer((socket)=>{
|
||||
socket.on("data",(msg)=>{
|
||||
data_iqdata = data_iqdata + msg.toString();
|
||||
if (data_iqdata.slice(-1) === "}") {
|
||||
iqdata = data_iqdata;
|
||||
|
@ -185,7 +217,8 @@ const server_iqdata = net.createServer((socket) => {
|
|||
console.log("Connection closed.");
|
||||
})
|
||||
});
|
||||
server_iqdata.listen(4002);
|
||||
server_iqdata.listen(config.network.ports.iqdata);
|
||||
|
||||
|
||||
// tcp listener falsetargets
|
||||
const server_falsetargets = net.createServer((socket) => {
|
||||
|
@ -201,4 +234,4 @@ const server_falsetargets = net.createServer((socket) => {
|
|||
console.log("Connection closed.");
|
||||
})
|
||||
});
|
||||
server_falsetargets.listen(4003);
|
||||
server_falsetargets.listen(4004);
|
||||
|
|
|
@ -14,13 +14,15 @@ process:
|
|||
buffer: 1.5
|
||||
overlap: 0
|
||||
ambiguity:
|
||||
delayMin: -10 # bins
|
||||
delayMax: 400 # bins
|
||||
dopplerMin: -200 # Hz
|
||||
dopplerMax: 200 # Hz
|
||||
# delay in bins
|
||||
delayMin: -10
|
||||
delayMax: 400
|
||||
# Doppler in Hz
|
||||
dopplerMin: -200
|
||||
dopplerMax: 200
|
||||
clutter:
|
||||
delayMin: -10 # bins
|
||||
delayMax: 400 # bins
|
||||
delayMin: -10
|
||||
delayMax: 400
|
||||
detection:
|
||||
pfa: 0.00001
|
||||
nGuard: 2
|
||||
|
@ -46,18 +48,31 @@ network:
|
|||
timestamp: 4000
|
||||
timing: 4001
|
||||
iqdata: 4002
|
||||
falsetargets: 4003
|
||||
config: 4003
|
||||
falsetargets: 4004
|
||||
|
||||
truth:
|
||||
asdb:
|
||||
adsb:
|
||||
enabled: false
|
||||
ip: 0.0.0.0
|
||||
port: 30000
|
||||
ip: 'adsb.30hours.dev'
|
||||
port: 80
|
||||
ais:
|
||||
enabled: false
|
||||
ip: 0.0.0.0
|
||||
port: 30001
|
||||
|
||||
location:
|
||||
rx:
|
||||
latitude: -34.9286
|
||||
longitude: 138.5999
|
||||
altitude: 50
|
||||
name: "Adelaide"
|
||||
tx:
|
||||
latitude: -34.9810
|
||||
longitude: 138.7081
|
||||
altitude: 750
|
||||
name: "Mount Lofty"
|
||||
|
||||
save:
|
||||
iq: false
|
||||
map: false
|
||||
|
|
|
@ -50,17 +50,30 @@ network:
|
|||
timestamp: 4000
|
||||
timing: 4001
|
||||
iqdata: 4002
|
||||
config: 4003
|
||||
|
||||
truth:
|
||||
asdb:
|
||||
adsb:
|
||||
enabled: false
|
||||
ip: 0.0.0.0
|
||||
port: 30000
|
||||
ip: 'adsb.30hours.dev'
|
||||
port: 80
|
||||
ais:
|
||||
enabled: false
|
||||
ip: 0.0.0.0
|
||||
port: 30001
|
||||
|
||||
location:
|
||||
rx:
|
||||
latitude: -34.9286
|
||||
longitude: 138.5999
|
||||
altitude: 50
|
||||
name: "Adelaide"
|
||||
tx:
|
||||
latitude: -34.9810
|
||||
longitude: 138.7081
|
||||
altitude: 750
|
||||
name: "Mount Lofty"
|
||||
|
||||
save:
|
||||
iq: true
|
||||
map: false
|
||||
|
|
|
@ -46,17 +46,30 @@ network:
|
|||
timestamp: 4000
|
||||
timing: 4001
|
||||
iqdata: 4002
|
||||
config: 4003
|
||||
|
||||
truth:
|
||||
asdb:
|
||||
adsb:
|
||||
enabled: false
|
||||
ip: 0.0.0.0
|
||||
port: 30000
|
||||
ip: 'adsb.30hours.dev'
|
||||
port: 80
|
||||
ais:
|
||||
enabled: false
|
||||
ip: 0.0.0.0
|
||||
port: 30001
|
||||
|
||||
location:
|
||||
rx:
|
||||
latitude: -34.9286
|
||||
longitude: 138.5999
|
||||
altitude: 50
|
||||
name: "Adelaide"
|
||||
tx:
|
||||
latitude: -34.9810
|
||||
longitude: 138.7081
|
||||
altitude: 750
|
||||
name: "Mount Lofty"
|
||||
|
||||
save:
|
||||
iq: true
|
||||
map: false
|
||||
|
|
|
@ -17,7 +17,7 @@ services:
|
|||
volumes:
|
||||
- ./config:/blah2/config
|
||||
- /opt/blah2/save:/blah2/save
|
||||
- /dev/shm:/dev/shm
|
||||
- /dev/shm:/dev/shm:z
|
||||
- /dev/usb:/dev/usb
|
||||
network_mode: host
|
||||
privileged: true
|
||||
|
@ -25,7 +25,7 @@ services:
|
|||
sh -c "/blah2/bin/blah2 -c config/config.yml"
|
||||
container_name: blah2
|
||||
|
||||
blah2_frontend:
|
||||
blah2_web:
|
||||
restart: always
|
||||
image: httpd:2.4
|
||||
ports:
|
||||
|
@ -40,7 +40,9 @@ services:
|
|||
restart: always
|
||||
build: ./api
|
||||
image: blah2_api
|
||||
ports:
|
||||
- 3000:8080
|
||||
volumes:
|
||||
- ./config:/usr/src/app/config
|
||||
network_mode: host
|
||||
command: >
|
||||
sh -c "node server.js /usr/src/app/config/config.yml"
|
||||
container_name: blah2-api
|
||||
|
|
19
host/README.md
Normal file
19
host/README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# blah2 Host
|
||||
|
||||
A reverse proxy to host blah2 on the internet.
|
||||
|
||||
## Description
|
||||
|
||||
This can be used to forward the radar to the internet. The radar front-end is at `localhost:49152`, and the API is located at `localhost:3000/api/<data>`. This reverse proxy forwards `localhost:49152` to a port of your choosing, and forwards `localhost:3000` to the same port at `/api/`.
|
||||
|
||||
## Usage
|
||||
|
||||
**docker-compose.yml**
|
||||
|
||||
- Change the output port from `8080` as desired in `docker-compose.yml`.
|
||||
- The environment variable `VIRTUAL_HOST=domain.tld` is only applicable if also using [jwilder/nginx](https://github.com/nginx-proxy/nginx-proxy).
|
||||
- If using [jwilder/nginx](https://github.com/nginx-proxy/nginx-proxy), ensure both containers are on the same network with `sudo docker network create <name>`. Otherwise the network configuration can be deleted.
|
||||
|
||||
**nginx.conf**
|
||||
|
||||
- Edit the `backend_ip` and `domain_name` variables in `nginx.conf`.
|
20
host/docker-compose.yml
Normal file
20
host/docker-compose.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
version: '3'
|
||||
|
||||
networks:
|
||||
nginx-web:
|
||||
external: true
|
||||
|
||||
services:
|
||||
httpd:
|
||||
restart: always
|
||||
image: nginx:1.25.2-alpine
|
||||
ports:
|
||||
- 8080:80
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./html:/usr/local/apache2/htdocs
|
||||
environment:
|
||||
- VIRTUAL_HOST=domain.tld
|
||||
networks:
|
||||
- nginx-web
|
||||
container_name: blah2
|
1
host/html/error.html
Normal file
1
host/html/error.html
Normal file
|
@ -0,0 +1 @@
|
|||
radar/down
|
58
host/nginx.conf
Normal file
58
host/nginx.conf
Normal file
|
@ -0,0 +1,58 @@
|
|||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
default_type application/octet-stream;
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
set $backend_ip localhost;
|
||||
set $domain_name localhost;
|
||||
|
||||
proxy_pass_header Content-Type;
|
||||
proxy_set_header X-Real-IP $domain_name;
|
||||
proxy_set_header X-Forwarded-For $domain_name;
|
||||
proxy_set_header Host $domain_name;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_connect_timeout 1;
|
||||
proxy_next_upstream error timeout http_500 http_502 http_503 http_504 http_404;
|
||||
proxy_intercept_errors on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://$backend_ip:49152;
|
||||
}
|
||||
|
||||
location ~ ^/(maxhold|api|stash)/(.*) {
|
||||
proxy_pass http://$backend_ip:3000/$1/$2;
|
||||
}
|
||||
|
||||
error_page 501 502 503 504 =200 /error.html;
|
||||
location = /error.html {
|
||||
root /usr/local/apache2/htdocs;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -8,6 +8,9 @@ var range_y = [];
|
|||
// setup API
|
||||
var urlTimestamp;
|
||||
var urlDetection;
|
||||
var urlAdsb;
|
||||
var urlAdsbLink;
|
||||
var urlConfig;
|
||||
if (isLocalHost) {
|
||||
urlTimestamp = '//' + host + ':3000/api/timestamp';
|
||||
} else {
|
||||
|
@ -23,10 +26,33 @@ if (isLocalHost) {
|
|||
} else {
|
||||
urlMap = '//' + host + urlMap;
|
||||
}
|
||||
if (isLocalHost) {
|
||||
urlAdsbLink = '//' + host + ':3000/api/adsb2dd';
|
||||
} else {
|
||||
urlAdsbLink = '//' + host + '/api/adsb2dd';
|
||||
}
|
||||
if (isLocalHost) {
|
||||
urlConfig = '//' + host + ':3000/api/config';
|
||||
} else {
|
||||
urlConfig = '//' + host + '/api/config';
|
||||
}
|
||||
urlTimestamp = urlTimestamp + '?timestamp=' + Date.now();
|
||||
urlDetection = urlDetection + '?timestamp=' + Date.now();
|
||||
urlMap = urlMap + '?timestamp=' + Date.now();
|
||||
|
||||
// get truth flag
|
||||
var isTruth = false;
|
||||
var configData = $.getJSON(urlConfig, function () { })
|
||||
.done(function (data_config) {
|
||||
if (data_config.truth.adsb.enabled === true) {
|
||||
isTruth = true;
|
||||
var adsbLinkData = $.getJSON(urlAdsbLink, function () { })
|
||||
.done(function (data) {
|
||||
urlAdsb = data.url;
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// setup plotly
|
||||
var layout = {
|
||||
autosize: true,
|
||||
|
@ -65,7 +91,8 @@ var layout = {
|
|||
ticksuffix: ' ',
|
||||
autosize: false,
|
||||
categoryorder: "total descending"
|
||||
}
|
||||
},
|
||||
showlegend: false
|
||||
};
|
||||
var config = {
|
||||
responsive: true,
|
||||
|
@ -82,6 +109,7 @@ var data = [
|
|||
}
|
||||
];
|
||||
var detection = [];
|
||||
var adsb = {};
|
||||
|
||||
Plotly.newPlot('data', data, layout, config);
|
||||
|
||||
|
@ -101,6 +129,23 @@ var intervalId = window.setInterval(function () {
|
|||
detection = data_detection;
|
||||
});
|
||||
|
||||
// get ADS-B data if enabled in config
|
||||
if (isTruth) {
|
||||
var adsbData = $.getJSON(urlAdsb, function () { })
|
||||
.done(function (data_adsb) {
|
||||
adsb['delay'] = [];
|
||||
adsb['doppler'] = [];
|
||||
adsb['flight'] = [];
|
||||
for (const aircraft in data_adsb) {
|
||||
if ('doppler' in data_adsb[aircraft]) {
|
||||
adsb['delay'].push(data_adsb[aircraft]['delay'])
|
||||
adsb['doppler'].push(data_adsb[aircraft]['doppler'])
|
||||
adsb['flight'].push(data_adsb[aircraft]['flight'])
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// get new map data
|
||||
var apiData = $.getJSON(urlMap, function () { })
|
||||
.done(function (data) {
|
||||
|
@ -136,17 +181,28 @@ var intervalId = window.setInterval(function () {
|
|||
opacity: 0.6
|
||||
}
|
||||
};
|
||||
var trace3 = {
|
||||
x: adsb.delay,
|
||||
y: adsb.doppler,
|
||||
mode: 'markers',
|
||||
type: 'scatter',
|
||||
marker: {
|
||||
size: 16,
|
||||
opacity: 0.6
|
||||
}
|
||||
};
|
||||
|
||||
var data_trace = [trace1, trace2];
|
||||
var data_trace = [trace1, trace2, trace3];
|
||||
Plotly.newPlot('data', data_trace, layout, config);
|
||||
}
|
||||
// case update plot
|
||||
else {
|
||||
var trace_update = {
|
||||
x: [data.delay, detection.delay],
|
||||
y: [data.doppler, detection.doppler],
|
||||
z: [data.data, []],
|
||||
zmax: [Math.max(13, data.maxPower), []]
|
||||
x: [data.delay, detection.delay, adsb.delay],
|
||||
y: [data.doppler, detection.doppler, adsb.doppler],
|
||||
z: [data.data, [], []],
|
||||
zmax: [Math.max(13, data.maxPower), [], []],
|
||||
text: [[], [], adsb.flight]
|
||||
};
|
||||
Plotly.update('data', trace_update);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
1543
lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run
Normal file
1543
lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run
Normal file
File diff suppressed because it is too large
Load diff
|
@ -61,7 +61,8 @@ int main(int argc, char **argv)
|
|||
|
||||
// setup capture
|
||||
uint32_t fs, fc;
|
||||
std::string type, path, replayFile;
|
||||
uint16_t port_capture;
|
||||
std::string type, path, replayFile, ip_capture;
|
||||
bool saveIq, state, loop;
|
||||
tree["capture"]["fs"] >> fs;
|
||||
tree["capture"]["fc"] >> fc;
|
||||
|
@ -71,6 +72,8 @@ int main(int argc, char **argv)
|
|||
tree["capture"]["replay"]["state"] >> state;
|
||||
tree["capture"]["replay"]["loop"] >> loop;
|
||||
tree["capture"]["replay"]["file"] >> replayFile;
|
||||
tree["network"]["ip"] >> ip_capture;
|
||||
tree["network"]["ports"]["api"] >> port_capture;
|
||||
Capture *capture = new Capture(type, fs, fc, path);
|
||||
if (state)
|
||||
{
|
||||
|
@ -85,7 +88,8 @@ int main(int argc, char **argv)
|
|||
|
||||
// run capture
|
||||
std::thread t1([&]{capture->process(buffer1, buffer2,
|
||||
tree["capture"]["device"]);});
|
||||
tree["capture"]["device"], ip_capture, port_capture);
|
||||
});
|
||||
|
||||
// setup process CPI
|
||||
double tCpi;
|
||||
|
|
|
@ -20,7 +20,8 @@ Capture::Capture(std::string _type, uint32_t _fs, uint32_t _fc, std::string _pat
|
|||
saveIq = false;
|
||||
}
|
||||
|
||||
void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config)
|
||||
void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config,
|
||||
std::string ip_capture, uint16_t port_capture)
|
||||
{
|
||||
std::cout << "Setting up device " + type << std::endl;
|
||||
|
||||
|
@ -31,7 +32,8 @@ void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config)
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
httplib::Client cli("http://127.0.0.1:3000");
|
||||
httplib::Client cli("http://" + ip_capture + ":"
|
||||
+ std::to_string(port_capture));
|
||||
httplib::Result res = cli.Get("/capture");
|
||||
|
||||
// if capture status changed
|
||||
|
|
|
@ -59,8 +59,11 @@ public:
|
|||
/// @param buffer1 Buffer for reference samples.
|
||||
/// @param buffer2 Buffer for surveillance samples.
|
||||
/// @param config Yaml config for device.
|
||||
/// @param ip_capture IP address of capture API.
|
||||
/// @param port_capture Port of capture API.
|
||||
/// @return Void.
|
||||
void process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config);
|
||||
void process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config,
|
||||
std::string ip_capture, uint16_t port_capture);
|
||||
|
||||
std::unique_ptr<Source> factory_source(const std::string& type,
|
||||
c4::yml::NodeRef config);
|
||||
|
|
67
src/capture/simulator/Simulator.cpp
Normal file
67
src/capture/simulator/Simulator.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "Simulator.h"
|
||||
|
||||
// constructor
|
||||
Simulator::Simulator(std::string _type, uint32_t _fc, uint32_t _fs,
|
||||
std::string _path, bool *_saveIq,
|
||||
uint32_t _n_min = 1000,
|
||||
std::string _falseTargetsConfigFilePath,
|
||||
std::string _configFilePath)
|
||||
: Source(_type, _fc, _fs, _path, _saveIq)
|
||||
{
|
||||
n_min = _n_min;
|
||||
u_int64_t total_samples = 0;
|
||||
false_targets_config_file_path = _falseTargetsConfigFilePath;
|
||||
config_file_path = _configFilePath;
|
||||
}
|
||||
|
||||
void Simulator::start()
|
||||
{
|
||||
}
|
||||
|
||||
void Simulator::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void Simulator::process(IqData *buffer1, IqData *buffer2)
|
||||
{
|
||||
const u_int32_t samples_per_iteration = 1000;
|
||||
|
||||
Target false_targets = Target(false_targets_config_file_path, config_file_path, fs, fc);
|
||||
while (true)
|
||||
{
|
||||
uint32_t n_start = buffer1->get_length();
|
||||
if (n_start < n_min)
|
||||
{
|
||||
|
||||
// create a random number generator
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> dis(-(2 ^ 14), 2 ^ 14);
|
||||
|
||||
buffer1->lock();
|
||||
buffer2->lock();
|
||||
for (uint16_t i = 0; i < samples_per_iteration; i++)
|
||||
{
|
||||
|
||||
buffer1->push_back({(double)dis(gen), (double)dis(gen)});
|
||||
try
|
||||
{
|
||||
std::complex<double> response = false_targets.process(buffer1);
|
||||
response += std::complex<double>((double)dis(gen), (double)dis(gen));
|
||||
buffer2->push_back(response);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
buffer2->push_back({(double)dis(gen), (double)dis(gen)});
|
||||
}
|
||||
}
|
||||
total_samples += samples_per_iteration;
|
||||
buffer1->unlock();
|
||||
buffer2->unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Simulator::replay(IqData *buffer1, IqData *buffer2, std::string file, bool loop)
|
||||
{
|
||||
}
|
85
src/capture/simulator/Simulator.h
Normal file
85
src/capture/simulator/Simulator.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/// @file Simulator.h
|
||||
/// @class Simulator
|
||||
/// @brief A class to generate simulated IQ data with false targets.
|
||||
/// @details This class generates simulated IQ data with false targets.
|
||||
/// It generates a random reference and surveillance signal and uses the
|
||||
/// Target class to add false targets to the surveillance signal.
|
||||
///
|
||||
/// @author bennysomers
|
||||
/// @todo Simulate a single false target
|
||||
/// @todo Simulate false targets
|
||||
/// @todo Simulate realistic target motion
|
||||
/// @todo Simulate N channels, instead of just 2
|
||||
/// @todo Add more waveforms (e.g. LFM).
|
||||
|
||||
#ifndef SIMULATOR_H
|
||||
#define SIMULATOR_H
|
||||
|
||||
#include "capture/Source.h"
|
||||
#include "Target.h"
|
||||
#include "data/IqData.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
#include <random>
|
||||
|
||||
class Simulator : public Source
|
||||
{
|
||||
private:
|
||||
/// @brief Number of samples to generate each loop.
|
||||
/// @details This is the threshold for the minimum number of samples
|
||||
/// left in the buffer before new samples will be generated.
|
||||
uint32_t n_min;
|
||||
|
||||
/// @brief Total number of samples generated.
|
||||
/// @details This is used to keep track of the total number of samples
|
||||
/// generated, so that the Doppler shift can be calculated.
|
||||
u_int64_t total_samples;
|
||||
|
||||
/// @brief Path to the false targets configuration file.
|
||||
std::string false_targets_config_file_path;
|
||||
|
||||
/// @brief Path to the radar configuration file.
|
||||
std::string config_file_path;
|
||||
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
/// @param type Type of source. = "IQSimulator"
|
||||
/// @param fc Center frequency (Hz).
|
||||
/// @param fs Sample rate (Hz).
|
||||
/// @param path Path to save IQ data.
|
||||
/// @param saveIq Pointer to boolean to save IQ data.
|
||||
/// @param n Number of samples.
|
||||
/// @return The object.
|
||||
Simulator(std::string type, uint32_t fc, uint32_t fs, std::string path,
|
||||
bool *saveIq, uint32_t n_min,
|
||||
std::string false_targets_config_file_path = "config/false_targets.yml",
|
||||
std::string config_file_path = "config/config.yml");
|
||||
|
||||
/// @brief Implement capture function on IQSimulator.
|
||||
/// @param buffer1 Pointer to reference buffer.
|
||||
/// @param buffer2 Pointer to surveillance buffer.
|
||||
/// @return Void.
|
||||
void process(IqData *buffer1, IqData *buffer2);
|
||||
|
||||
/// @brief Call methods to start capture.
|
||||
/// @return Void.
|
||||
void start();
|
||||
|
||||
/// @brief Call methods to gracefully stop capture.
|
||||
/// @return Void.
|
||||
void stop();
|
||||
|
||||
/// @brief Implement replay function on IQSimulator.
|
||||
/// @param buffer1 Pointer to reference buffer.
|
||||
/// @param buffer2 Pointer to surveillance buffer.
|
||||
/// @param file Path to file to replay data from.
|
||||
/// @param loop True if samples should loop at EOF.
|
||||
/// @return Void.
|
||||
void replay(IqData *buffer1, IqData *buffer2, std::string file, bool loop);
|
||||
};
|
||||
|
||||
#endif
|
226
src/capture/simulator/Target.cpp
Normal file
226
src/capture/simulator/Target.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
#include "Target.h"
|
||||
|
||||
// this is straight up copied from blah2.cpp, but I don't know the best way to access that function here.
|
||||
// edit: put it in utilities?
|
||||
std::string ryml_get_file_copy(const char *filename);
|
||||
|
||||
// constants
|
||||
const std::string Target::VALID_TYPE[2] = {"static", "moving_radar"};
|
||||
const std::string Target::VALID_STATE[1] = {"active"};
|
||||
|
||||
// constructor
|
||||
Target::Target(std::string false_tgt_config_path, std::string config_path, uint32_t fs, uint32_t fc)
|
||||
{
|
||||
// Read in the false targets config file
|
||||
std::string config = ryml_get_file_copy(false_tgt_config_path.c_str());
|
||||
ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(config));
|
||||
|
||||
// Create a FalseTarget object for each target in the config file
|
||||
for (auto target_node : tree["targets"].children())
|
||||
{
|
||||
if (target_node["state"].val() == VALID_STATE[0])
|
||||
{
|
||||
try
|
||||
{
|
||||
targets.push_back(FalseTarget(target_node, fs, fc));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the socket using details from the config file.
|
||||
config = ryml_get_file_copy(config_path.c_str());
|
||||
tree = ryml::parse_in_arena(ryml::to_csubstr(config));
|
||||
|
||||
std::string ip;
|
||||
uint16_t port;
|
||||
|
||||
tree["network"]["ip"] >> ip;
|
||||
tree["network"]["ports"]["falsetargets"] >> port;
|
||||
|
||||
socket = new Socket(ip, port);
|
||||
|
||||
sample_counter = 0;
|
||||
}
|
||||
|
||||
std::complex<double> Target::process(IqData *ref_buffer)
|
||||
{
|
||||
std::complex<double> response = std::complex<double>(0, 0);
|
||||
// loop through each target
|
||||
for (auto &target : targets)
|
||||
{
|
||||
response += target.process(ref_buffer);
|
||||
}
|
||||
|
||||
// output false target truth
|
||||
if (sample_counter % 100000 == 0)
|
||||
{
|
||||
rapidjson::Document document;
|
||||
document.SetObject();
|
||||
rapidjson::Document::AllocatorType &allocator = document.GetAllocator();
|
||||
rapidjson::Value json_false_targets(rapidjson::kArrayType);
|
||||
for (auto target : targets)
|
||||
{
|
||||
json_false_targets.PushBack(target.to_json(allocator), allocator);
|
||||
}
|
||||
|
||||
document.AddMember("false_targets", json_false_targets, allocator);
|
||||
rapidjson::StringBuffer strbuf;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
|
||||
writer.SetMaxDecimalPlaces(2);
|
||||
document.Accept(writer);
|
||||
|
||||
socket->sendData(strbuf.GetString());
|
||||
}
|
||||
|
||||
sample_counter++;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
double FalseTarget::get_range()
|
||||
{
|
||||
return range;
|
||||
}
|
||||
|
||||
void FalseTarget::set_range(double _range)
|
||||
{
|
||||
range = _range;
|
||||
delay = range / Constants::c;
|
||||
delay_samples = delay * fs;
|
||||
}
|
||||
|
||||
double FalseTarget::get_delay()
|
||||
{
|
||||
return delay;
|
||||
}
|
||||
|
||||
void FalseTarget::set_delay(double _delay)
|
||||
{
|
||||
delay = _delay;
|
||||
range = delay * Constants::c;
|
||||
delay_samples = delay * fs;
|
||||
}
|
||||
|
||||
FalseTarget::FalseTarget(c4::yml::NodeRef target_node, uint32_t _fs, uint32_t _fc)
|
||||
{
|
||||
|
||||
target_node["id"] >> id;
|
||||
target_node["type"] >> type;
|
||||
fs = _fs;
|
||||
fc = _fc;
|
||||
sample_counter = 0;
|
||||
|
||||
if (type == TgtGen::VALID_TYPE[0])
|
||||
{
|
||||
target_node["location"]["range"] >> range;
|
||||
delay = range / Constants::c;
|
||||
delay_samples = delay * fs;
|
||||
target_node["velocity"]["doppler"] >> doppler;
|
||||
target_node["rcs"] >> rcs;
|
||||
}
|
||||
else if (type == TgtGen::VALID_TYPE[1])
|
||||
{
|
||||
target_node["location"]["range"] >> range;
|
||||
start_range = range;
|
||||
delay = range / Constants::c;
|
||||
delay_samples = delay * fs;
|
||||
target_node["velocity"]["doppler"] >> doppler;
|
||||
target_node["velocity"]["dopplerRate"] >> doppler_rate;
|
||||
target_node["rcs"] >> rcs;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::invalid_argument("Invalid target type");
|
||||
}
|
||||
}
|
||||
|
||||
std::complex<double> FalseTarget::process(IqData *ref_buffer)
|
||||
{
|
||||
uint32_t buffer_length = ref_buffer->get_length();
|
||||
std::complex<double> response = 0;
|
||||
try
|
||||
{
|
||||
response = Conversions::db2lin(rcs) * ref_buffer->get_sample(buffer_length - delay_samples);
|
||||
response *= std::exp(std::polar<double>(1, 2 * M_PI * doppler * buffer_length / fs));
|
||||
|
||||
if (type == TgtGen::VALID_TYPE[1])
|
||||
{
|
||||
double range_rate = -1 * doppler * Constants::c / 2.0 / fc;
|
||||
set_range(range + (range_rate / fs));
|
||||
|
||||
// very basic PD controller
|
||||
// will need tuning for different fs
|
||||
doppler_rate += 0.0000001 * (range - start_range) / start_range; // target tends towards start_range
|
||||
// doppler_rate -= doppler / std::abs(doppler) / std::max(std::abs(doppler), 0.1) / 100; // target tends towards 0 Doppler
|
||||
doppler_rate = std::clamp(doppler_rate, -5.0, 5.0); // clamp to a reasonable value
|
||||
doppler += doppler_rate / fs; // update doppler
|
||||
doppler = std::clamp(doppler,
|
||||
std::max(-range, -250.0),
|
||||
std::min(range, 250.0)); // clamp to range
|
||||
}
|
||||
|
||||
sample_counter++;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
rapidjson::Value FalseTarget::to_json(rapidjson::Document::AllocatorType &allocator)
|
||||
{
|
||||
|
||||
rapidjson::Value target(rapidjson::kObjectType);
|
||||
|
||||
target.AddMember("id", id, allocator);
|
||||
target.AddMember("type", rapidjson::Value(type.c_str(), allocator).Move(), allocator);
|
||||
|
||||
try
|
||||
{
|
||||
if (type == TgtGen::VALID_TYPE[0])
|
||||
{
|
||||
target.AddMember("range", range, allocator);
|
||||
target.AddMember("delay", delay, allocator);
|
||||
target.AddMember("delay_samples", delay_samples, allocator);
|
||||
target.AddMember("doppler", doppler, allocator);
|
||||
target.AddMember("rcs", rcs, allocator);
|
||||
}
|
||||
else if (type == TgtGen::VALID_TYPE[1])
|
||||
{
|
||||
target.AddMember("range", range, allocator);
|
||||
target.AddMember("start_range", start_range, allocator);
|
||||
target.AddMember("delay", delay, allocator);
|
||||
target.AddMember("delay_samples", delay_samples, allocator);
|
||||
target.AddMember("doppler", doppler, allocator);
|
||||
target.AddMember("doppler_rate", doppler_rate, allocator);
|
||||
target.AddMember("rcs", rcs, allocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::invalid_argument("Invalid target type");
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
std::string ryml_get_file_copy(const char *filename)
|
||||
{
|
||||
std::ifstream in(filename, std::ios::in | std::ios::binary);
|
||||
if (!in)
|
||||
{
|
||||
std::cerr << "could not open " << filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::ostringstream contents;
|
||||
contents << in.rdbuf();
|
||||
return contents.str();
|
||||
}
|
143
src/capture/simulator/Target.h
Normal file
143
src/capture/simulator/Target.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
/// @file TgtGen.h
|
||||
/// @class TgtGen
|
||||
/// @brief A class to generate false targets.
|
||||
|
||||
/// @details
|
||||
/// Static Targets: remain at a fixed range/delay and Doppler.
|
||||
|
||||
/// @author bennysomers
|
||||
/// @todo Simulate a false target moving in radar coordinates
|
||||
/// @todo Simulate a false target moving in spatial coordinates
|
||||
|
||||
#ifndef TGTGEN_H
|
||||
#define TGTGEN_H
|
||||
|
||||
#include "data/IqData.h"
|
||||
#include "utilities/Conversions.h"
|
||||
#include "data/meta/Constants.h"
|
||||
#include "process/utility/Socket.h"
|
||||
|
||||
#include <ryml/ryml.hpp>
|
||||
#include <ryml/ryml_std.hpp>
|
||||
#include <c4/format.hpp>
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include "rapidjson/filewritestream.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <complex>
|
||||
#include <math.h>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
class FalseTarget
|
||||
{
|
||||
private:
|
||||
/// @brief fs
|
||||
uint32_t fs;
|
||||
|
||||
/// @brief fc
|
||||
uint32_t fc;
|
||||
|
||||
/// @brief Target delay
|
||||
double delay;
|
||||
|
||||
/// @brief Target delay in samples
|
||||
uint32_t delay_samples;
|
||||
|
||||
/// @brief Target range
|
||||
double range;
|
||||
|
||||
/// @brief Target starting range
|
||||
double start_range;
|
||||
|
||||
/// @brief Sample counter
|
||||
uint64_t sample_counter;
|
||||
|
||||
public:
|
||||
/// @brief Target type.
|
||||
std::string type;
|
||||
|
||||
/// @brief Target Doppler
|
||||
double doppler;
|
||||
|
||||
/// @brief Target Doppler Rate
|
||||
double doppler_rate;
|
||||
|
||||
/// @brief Target RCS
|
||||
double rcs;
|
||||
|
||||
/// @brief Target ID
|
||||
u_int32_t id;
|
||||
|
||||
/// @brief Constructor for targets.
|
||||
/// @return The object.
|
||||
FalseTarget(c4::yml::NodeRef target_node, uint32_t _fs, uint32_t _fc);
|
||||
|
||||
/// @brief Generate the signal from a false target.
|
||||
/// @param ref_buffer Pointer to reference buffer.
|
||||
/// @return Target reflection signal.
|
||||
std::complex<double> process(IqData *ref_buffer);
|
||||
|
||||
/// @brief Getter for range.
|
||||
/// @return Range in meters.
|
||||
double get_range();
|
||||
|
||||
/// @brief Setter for range.
|
||||
/// @param range Range in meters.
|
||||
void set_range(double range);
|
||||
|
||||
/// @brief Getter for delay.
|
||||
/// @return Delay in seconds.
|
||||
double get_delay();
|
||||
|
||||
/// @brief Setter for delay.
|
||||
/// @param delay Delay in seconds.
|
||||
void set_delay(double delay);
|
||||
|
||||
/// @brief Outputs false target truth as JSON
|
||||
/// @return JSON string.
|
||||
rapidjson::Value to_json(rapidjson::Document::AllocatorType &allocator);
|
||||
};
|
||||
|
||||
class TgtGen
|
||||
{
|
||||
private:
|
||||
/// @brief Vector of false targets.
|
||||
std::vector<FalseTarget> targets;
|
||||
|
||||
/// @brief Socket to send false target data.
|
||||
Socket *socket;
|
||||
|
||||
/// @brief Sample counter
|
||||
uint64_t sample_counter;
|
||||
|
||||
public:
|
||||
/// @brief The valid false target types.
|
||||
static const std::string VALID_TYPE[2];
|
||||
|
||||
/// @brief The valid false target states.
|
||||
static const std::string VALID_STATE[1];
|
||||
|
||||
/// @brief Constructor.
|
||||
/// @param false_tgt_config_path Path to false targets configuration file.
|
||||
/// @param config_path Path to blah2 config file.
|
||||
/// @param fs Sample rate (Hz).
|
||||
/// @param fc Center frequency (Hz).
|
||||
/// @return The object.
|
||||
TgtGen(std::string false_tgt_config_path, std::string config_path, uint32_t fs, uint32_t fc);
|
||||
|
||||
/// @brief Generate the signal from all false targets.
|
||||
/// @param ref_buffer Pointer to reference buffer.
|
||||
/// @return Targets reflection signal.
|
||||
std::complex<double> process(IqData *ref_buffer);
|
||||
};
|
||||
|
||||
#endif
|
||||
std::string ryml_get_file(const char *filename);
|
|
@ -6,7 +6,12 @@ const uint32_t Socket::MTU = 1024;
|
|||
|
||||
Socket::Socket(const std::string& ip, uint16_t port)
|
||||
: endpoint(asio::ip::address::from_string(ip), port), socket(io_context) {
|
||||
socket.connect(endpoint);
|
||||
try {
|
||||
socket.connect(endpoint);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error connecting to endpoint: " << e.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Socket::~Socket()
|
||||
|
|
Loading…
Reference in a new issue