Skip to content

Commit

Permalink
Add libacars integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy committed Jan 24, 2025
1 parent bcc42b2 commit 8f18494
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 8 deletions.
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ set(CMAKE_AUTOUIC ON)
find_package(Qt6 COMPONENTS Concurrent Core Multimedia Network)
find_package(PkgConfig)

find_path(ZeroMQ_INCLUDE_DIR NAMES zmq.h PATHS ${PC_ZeroMQ_INCLUDE_DIRS})
find_library(ZeroMQ_LIBRARIES NAMES zmq PATHS ${PC_ZeroMQ_LIBRARY_DIRS})
pkg_check_modules(ZeroMQ REQUIRED libzmq)

set(COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/common/)
find_file(COMMON_NOTIFIER_SOURCE_FILE notifier.cpp ${COMMON_INCLUDE_DIR})
Expand Down
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@
[![Activity](https://img.shields.io/github/commit-activity/m/airframesio/aero-cli)](https://github.com/airframesio/aero-cli/pulse)
[![Discord](https://img.shields.io/discord/1067697487927853077?logo=discord)](https://discord.gg/8Ksch7zE)


## Example
Running `aero-cli` is similar to running SDRReceiver and JAERO: `aero-publish` is interchangeable with SDRReceiver and `aero-decode` is interchangeable with JAERO.

To run `aero-publish`, make sure to at the minimum include the SoapySDR driver string `driver=rtlsdr` with an optional `,serial=NNNNNNNN` appended for binding with specific SDR in the event of many. `sdr_54W_all.ini` can be found in `samples_ini/` withint the SDRReceiver repository.
```bash
aero-publish -d driver=rtlsdr --enable-biast sdr_54W_all.ini
```

To run `aero-decode`:
```bash
aero-decode -v -p tcp://127.0.0.1:6004 -t VFO52 -b 10500 -f jsondump=tcp://127.0.0.1:4444
```

## TODO
- [x] Implement C-band support (1200/10500)
- [ ] Implement test harness that streams audio from audio-out into a ZeroMQ topic for samples testing (mostly for burst mode)
- [x] Implement test harness that streams audio from audio-out into a ZeroMQ topic for samples testing (mostly for burst mode)
- [x] Cut out plane registration database code from AeroL
- [x] Implement ACARS frame forwarding functionality
- [x] Implement translation of ACARSItem to JSON
- [x] libacars integration
- [ ] SBS forwarding
- [ ] AMBE decoding?
- [ ] Long term test to ensure processor and memory usage is within expectations
- [ ] libacars integration?

## Requirements
Other configurations not mentioned may work but below is the configuration used for development and testing:
Expand All @@ -31,6 +36,12 @@ Other configurations not mentioned may work but below is the configuration used
* libcorrect (commit f5a28c74fba7a99736fe49d3a5243eca29517ae9)
* QT 6.4+ (Core, Concurrent, Multimedia)

Unfortunately, the audio test harness requires Qt 5 in order to gain access to `monitor` audio inputs:
* Python 3
* PyQt5
* PyQt5 QtMultimedia
* PyZMQ

## Credits
* JAERO team
* SDRReceiver team
6 changes: 4 additions & 2 deletions decode/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
find_library(libcorrect_LIBRARIES NAMES correct PATHS ${PC_libcorrect_LIBRARY_DIRS})

include_directories(${ZeroMQ_INCLUDE_DIRS} ${COMMON_INCLUDE_DIR})
pkg_check_modules(LIBACARS REQUIRED libacars-2)

include_directories(${LIBACARS_INCLUDE_DIRS} ${ZeroMQ_INCLUDE_DIRS} ${COMMON_INCLUDE_DIR})

add_executable(
aero-decode
Expand All @@ -24,5 +26,5 @@ add_executable(
${COMMON_NOTIFIER_SOURCE_FILE}
${COMMON_LOGGER_SOURCE_FILE}
)
target_link_libraries(aero-decode PRIVATE ${ZeroMQ_LIBRARIES} ${libcorrect_LIBRARIES} Qt6::Concurrent Qt6::Core Qt6::Network)
target_link_libraries(aero-decode PRIVATE ${ZeroMQ_LIBRARIES} ${LIBACARS_LIBRARIES} ${libcorrect_LIBRARIES} Qt6::Concurrent Qt6::Core Qt6::Network)

3 changes: 3 additions & 0 deletions decode/aerol.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "jconvolutionalcodec.h"
#include <QDateTime>
#include <QDebug>
#include <QJsonObject>
#include <QList>
#include <QObject>
#include <QPointer>
Expand Down Expand Up @@ -176,6 +177,8 @@ class ACARSItem : public DBase {
bool hastext;
bool moretocome;
QString message;
QJsonObject parsed;

void clear() {
isuitem.clear();
valid = false;
Expand Down
64 changes: 63 additions & 1 deletion decode/decode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,72 @@
#include <QJsonDocument>
#include <QTcpSocket>
#include <QUdpSocket>
#include <libacars/acars.h>
#include <libacars/libacars.h>
#include <libacars/vstring.h>
#include <zmq.h>

#include "decode.h"
#include "logger.h"
#include "output.h"

bool libacarsDecode(ACARSItem &item) {
QByteArray ba;
la_proto_node *node = nullptr;
la_vstring *vstr = nullptr;
la_msg_dir msg_dir = item.downlink ? LA_MSG_DIR_AIR2GND : LA_MSG_DIR_GND2AIR;
bool status = false;

if (item.message.isEmpty())
return false;

if (item.downlink) {
ba = item.message.mid(10).toLatin1();
} else {
// look for a sublabel and if found strip the sublabel(s) from the message
// before decoding
ba = item.message.toLatin1();
char sublabel[3];
char mfi[3];
int offset = ::la_acars_extract_sublabel_and_mfi(
item.LABEL.data(), msg_dir, ba.data(), strlen(ba.data()), sublabel,
mfi);

if (offset > 0) {
ba = "/" + item.message.right(item.message.length() - offset)
.replace("- #" + QString(sublabel), "")
.trimmed()
.toLatin1();
} else {
ba = item.message.toLatin1();
}
}

if (ba.isEmpty())
return false;

node = ::la_acars_decode_apps(item.LABEL.data(), ba.data(), msg_dir);
if (node == nullptr)
goto exit;

vstr = ::la_proto_tree_format_json(nullptr, node);
if (vstr == nullptr)
goto exit;

item.parsed = QJsonDocument::fromJson(QString(vstr->str).toUtf8()).object();

::la_vstring_destroy(vstr, true);

status = true;

exit:
if (node != nullptr) {
::la_proto_tree_destroy(node);
}

return status;
}

Decoder::Decoder(const QString &station_id, const QString &publisher,
const QString &topic, const QString &format, int bitRate,
bool burstMode, const QString &rawForwarders,
Expand Down Expand Up @@ -65,7 +125,7 @@ Decoder::Decoder(const QString &station_id, const QString &publisher,
burstMskSettings.Fs = 48000;
burstMskSettings.fb = 1200;
burstMskSettings.lockingbw = 10500;

burstMskDemod = new BurstMskDemodulator(this);
burstMskDemod->setAFC(true);
burstMskDemod->setCPUReduce(false);
Expand Down Expand Up @@ -338,6 +398,8 @@ void Decoder::forwarderConsumer() {
sendBuffer.pop_front();
sendBufferRwLock.unlock();

libacarsDecode(item);

for (auto target : forwarders) {
if (target != nullptr) {
QString *out = toOutputFormat(target->getFormat(), stationId,
Expand Down
1 change: 1 addition & 0 deletions decode/hunter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ SignalHunter::SignalHunter(quint32 maxTries, QObject *parent)
this->fullScans = 0;
this->iterationsSinceSignal = 0;
this->lastDcd = false;
this->enabled = true;
}

SignalHunter::~SignalHunter() {}
Expand Down
7 changes: 7 additions & 0 deletions decode/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ QString *toOutputFormat(OutputFormat fmt, const QString &station_id,
} else {
acars["msg_text"] = message;
}

if (!item.parsed.isEmpty()) {
for (auto it = item.parsed.constBegin();
it != item.parsed.constEnd(); it++) {
acars.insert(it.key(), it.value());
}
}
}

isu["acars"] = QJsonValue(acars);
Expand Down

0 comments on commit 8f18494

Please sign in to comment.