Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,49 @@ target_link_libraries(testClutterFft PRIVATE
set_target_properties(testClutterFft PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")

add_executable(testJsonKm
test/unit/data/TestJsonKm.cpp
src/data/Map.cpp
src/data/Detection.cpp
)
# armadillo is linked only to pick up the vcpkg include path, which is
# where rapidjson lives; RAPIDJSON_INCLUDE_DIRS is NOTFOUND in this build.
target_link_libraries(testJsonKm PRIVATE
armadillo
)
set_target_properties(testJsonKm PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")

add_executable(testAmbiguityIndexing
test/unit/process/ambiguity/TestAmbiguityIndexing.cpp
src/data/Map.cpp
)
# armadillo is linked only to pick up the vcpkg include path, which is
# where rapidjson lives; RAPIDJSON_INCLUDE_DIRS is NOTFOUND in this build.
target_link_libraries(testAmbiguityIndexing PRIVATE
armadillo
)
set_target_properties(testAmbiguityIndexing PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")

add_executable(testCfarHoist
test/unit/process/detection/TestCfarHoist.cpp
src/process/detection/CfarDetector1D.cpp
src/data/Map.cpp
src/data/Detection.cpp
)
# armadillo is linked only to pick up the vcpkg include path, which is
# where rapidjson lives; RAPIDJSON_INCLUDE_DIRS is NOTFOUND in this build.
target_link_libraries(testCfarHoist PRIVATE
armadillo
)
set_target_properties(testCfarHoist PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_TEST_UNIT_DIR}")

# TODO: Unsure if will be using CTest.
add_test(NAME testAmbiguity COMMAND testAmbiguity)
add_test(NAME testTracker COMMAND testTracker)
add_test(NAME testClutterFft COMMAND testClutterFft)
add_test(NAME testJsonKm COMMAND testJsonKm)
add_test(NAME testAmbiguityIndexing COMMAND testAmbiguityIndexing)
add_test(NAME testCfarHoist COMMAND testCfarHoist)
6 changes: 2 additions & 4 deletions src/blah2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,7 @@ int main(int argc, char **argv)
socket_iqdata.sendData(jsonIqData);

// output map data
mapJson = map->to_json(time[0]/1000);
mapJson = map->delay_bin_to_km(mapJson, fs);
mapJson = map->to_json_km(time[0]/1000, fs);
if (saveMap)
{
map->save(mapJson, saveMapPath);
Expand All @@ -313,8 +312,7 @@ int main(int argc, char **argv)
// output detection data
if (isDetection)
{
detectionJson = detection->to_json(time[0]/1000);
detectionJson = detection->delay_bin_to_km(detectionJson, fs);
detectionJson = detection->to_json_km(time[0]/1000, fs);
socket_detection.sendData(detectionJson);
}

Expand Down
39 changes: 39 additions & 0 deletions src/data/Detection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,45 @@ std::string Detection::to_json(uint64_t timestamp)
return strbuf.GetString();
}

std::string Detection::to_json_km(uint64_t timestamp, uint32_t fs)
{
// Single pass, matching to_json() + delay_bin_to_km() byte for byte.
rapidjson::StringBuffer strbuf;
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
writer.SetMaxDecimalPlaces(2);

writer.StartObject();
writer.Key("timestamp");
writer.Uint64(timestamp);

writer.Key("delay");
writer.StartArray();
for (size_t i = 0; i < delay.size(); i++)
{
writer.Double(1.0 * delay[i] * (Constants::c / (double)fs) / 1000);
}
writer.EndArray();

writer.Key("doppler");
writer.StartArray();
for (size_t i = 0; i < get_nDetections(); i++)
{
writer.Double(doppler[i]);
}
writer.EndArray();

writer.Key("snr");
writer.StartArray();
for (size_t i = 0; i < get_nDetections(); i++)
{
writer.Double(snr[i]);
}
writer.EndArray();
writer.EndObject();

return strbuf.GetString();
}

std::string Detection::delay_bin_to_km(std::string json, uint32_t fs)
{
rapidjson::Document document;
Expand Down
8 changes: 8 additions & 0 deletions src/data/Detection.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ class Detection
/// @return JSON string.
std::string delay_bin_to_km(std::string json, uint32_t fs);

/// @brief Serialise to JSON with the delay axis already in km.
/// @details Equivalent to delay_bin_to_km(to_json(timestamp), fs) but in a
/// single pass, without building or re-parsing a DOM.
/// @param timestamp Timestamp of the detections (ms).
/// @param fs Sampling frequency (Hz).
/// @return JSON string.
std::string to_json_km(uint64_t timestamp, uint32_t fs);

/// @brief Append the detections to a save file.
/// @param json JSON string of detections and metadata.
/// @param path Path of file to save.
Expand Down
5 changes: 5 additions & 0 deletions src/data/IqData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ std::deque<std::complex<double>> IqData::get_data()
return *data;
}

const std::deque<std::complex<double>> &IqData::view_data() const
{
return *data;
}

void IqData::push_back(std::complex<double> sample)
{
if (data->size() < n)
Expand Down
8 changes: 8 additions & 0 deletions src/data/IqData.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ class IqData
/// @return IQ data.
std::deque<std::complex<double>> get_data();

/// @brief Read-only view of the data, copying nothing.
/// @details Prefer this to get_data() on the hot path: a CPI is a million
/// samples, so a copy is 16 MB and roughly 31,000 deque-chunk allocations.
/// The caller must not hold the reference across anything that mutates the
/// queue (push_back, pop_front, clear).
/// @return Const reference to the IQ data.
const std::deque<std::complex<double>> &view_data() const;

/// @brief Push a sample to the queue.
/// @param sample A single sample.
/// @return Void.
Expand Down
61 changes: 59 additions & 2 deletions src/data/Map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Map<T>::Map(uint32_t _nRows, uint32_t _nCols)
}

template <class T>
void Map<T>::set_row(uint32_t i, std::vector<T> row)
void Map<T>::set_row(uint32_t i, const std::vector<T> &row)
{
//data[i].swap(row);
for (uint32_t j = 0; j < nCols; j++)
Expand All @@ -31,7 +31,7 @@ void Map<T>::set_row(uint32_t i, std::vector<T> row)
}

template <class T>
void Map<T>::set_col(uint32_t i, std::vector<T> col)
void Map<T>::set_col(uint32_t i, const std::vector<T> &col)
{
for (uint32_t j = 0; j < nRows; j++)
{
Expand Down Expand Up @@ -163,6 +163,63 @@ std::string Map<T>::to_json(uint64_t timestamp)
return strbuf.GetString();
}

template <class T>
std::string Map<T>::to_json_km(uint64_t timestamp, uint32_t fs)
{
// One pass, no DOM. The pair to_json() + delay_bin_to_km() serialised the
// whole map, parsed all of it back, rewrote the delay axis and serialised
// again, so a 301 x 411 map went through the writer twice and the parser
// once to convert 411 delay values. Key order and SetMaxDecimalPlaces match
// the old pair exactly, so the bytes are identical.
rapidjson::StringBuffer strbuf;
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
writer.SetMaxDecimalPlaces(2);

writer.StartObject();
writer.Key("timestamp");
writer.Uint64(timestamp);
writer.Key("nRows");
writer.Uint(nRows);
writer.Key("nCols");
writer.Uint(nCols);
writer.Key("noisePower");
writer.Double(noisePower);
writer.Key("maxPower");
writer.Double(maxPower);

writer.Key("delay");
writer.StartArray();
for (size_t i = 0; i < delay.size(); i++)
{
writer.Double(1.0 * delay[i] * (Constants::c / (double)fs) / 1000);
}
writer.EndArray();

writer.Key("doppler");
writer.StartArray();
for (uint32_t i = 0; i < get_nRows(); i++)
{
writer.Double(doppler[i]);
}
writer.EndArray();

writer.Key("data");
writer.StartArray();
for (size_t i = 0; i < data.size(); i++)
{
writer.StartArray();
for (size_t j = 0; j < data[i].size(); j++)
{
writer.Double(10 * std::log10(std::abs(data[i][j])) - noisePower);
}
writer.EndArray();
}
writer.EndArray();
writer.EndObject();

return strbuf.GetString();
}

template <class T>
std::string Map<T>::delay_bin_to_km(std::string json, uint32_t fs)
{
Expand Down
12 changes: 10 additions & 2 deletions src/data/Map.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ class Map
/// @param i Index of row to update.
/// @param row Data to update.
/// @return Void.
void set_row(uint32_t i, std::vector<T> row);
void set_row(uint32_t i, const std::vector<T> &row);

/// @brief Update a column in the 2D map.
/// @param i Index of column to update.
/// @param col Data to update.
/// @return Void.
void set_col(uint32_t i, std::vector<T> col);
void set_col(uint32_t i, const std::vector<T> &col);

/// @brief Create map metrics (noise power, dynamic range).
/// @return Void.
Expand Down Expand Up @@ -104,6 +104,14 @@ class Map
/// @return JSON string.
std::string delay_bin_to_km(std::string json, uint32_t fs);

/// @brief Serialise to JSON with the delay axis already in km.
/// @details Equivalent to delay_bin_to_km(to_json(timestamp), fs) but in a
/// single pass, without building or re-parsing a DOM.
/// @param timestamp Timestamp of the map (ms).
/// @param fs Sampling frequency (Hz).
/// @return JSON string.
std::string to_json_km(uint64_t timestamp, uint32_t fs);

/// @brief Append the map to a save file.
/// @param json JSON string of map and metadata.
/// @param path Path of file to save.
Expand Down
34 changes: 12 additions & 22 deletions src/process/ambiguity/Ambiguity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ Ambiguity::Ambiguity(int32_t _delayMin, int32_t _delayMax,
if (_roundHamming) {
nfft = next_hamming(nfft);
}
dataCorr.resize(2 * nDelayBins + 1);

// compute FFTW plans in constructor
dataXi.resize(nfft);
Expand Down Expand Up @@ -128,44 +127,35 @@ Map<std::complex<double>> *Ambiguity::process(IqData *x, IqData *y)

fftw_execute(fftZi);

// extract center of corr
// Extract the centre of the correlation straight into the map. The old
// dataCorr staging array copied 2*nDelayBins+1 values out of dataZi so
// that a single window could be read back out of it; that window is just
// dataZi at (j + delayMin), wrapped, so neither the staging array nor the
// intermediate corr vector is needed.
for (uint16_t j = 0; j < nDelayBins; j++)
{
dataCorr[j] = dataZi[nfft - nDelayBins + j];
const int64_t k = int64_t(j) + delayMin;
map->data[i][j] = dataZi[k < 0 ? k + nfft : k];
}
for (uint16_t j = 0; j < nDelayBins + 1; j++)
{
dataCorr[j + nDelayBins] = dataZi[j];
}

// cast from std::complex to std::vector
corr.clear();
for (uint16_t j = 0; j < nDelayBins; j++)
{
corr.push_back(dataCorr[nDelayBins + delayMin + j - 1 + 1]);
}

map->set_row(i, corr);
}

// doppler processing
for (uint16_t i = 0; i < nDelayBins; i++)
{
delayProfile = map->get_col(i);
// Read and write the column in place. get_col() built and returned a fresh
// vector per delay bin, and set_col() took another by value, so a 301-deep
// column was copied four times over to be transformed once.
for (uint16_t j = 0; j < nDopplerBins; j++)
{
dataDoppler[j] = {delayProfile[j].real(), delayProfile[j].imag()};
dataDoppler[j] = map->data[j][i];
}

fftw_execute(fftDoppler);

corr.clear();
for (uint16_t j = 0; j < nDopplerBins; j++)
{
corr.push_back(dataDoppler[(j + int(nDopplerBins / 2) + 1) % nDopplerBins]);
map->data[j][i] = dataDoppler[(j + int(nDopplerBins / 2) + 1) % nDopplerBins];
}

map->set_col(i, corr);
}

return map.get();
Expand Down
3 changes: 0 additions & 3 deletions src/process/ambiguity/Ambiguity.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ class Ambiguity
std::vector<Complex> dataXi;
std::vector<Complex> dataYi;
std::vector<Complex> dataZi;
std::vector<Complex> dataCorr;
std::vector<Complex> dataDoppler;
/// @}

Expand All @@ -111,8 +110,6 @@ class Ambiguity

/// @brief Vector storage for ambiguity processing
/// @{
std::vector<Complex> corr;
std::vector<Complex> delayProfile;
/// @}

/// @brief Map to store result.
Expand Down
7 changes: 5 additions & 2 deletions src/process/clutter/WienerHopf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ WienerHopf::~WienerHopf()
bool WienerHopf::process(IqData *x, IqData *y)
{
uint32_t i, j;
xData = x->get_data();
yData = y->get_data();
// Views, not copies: each of these was 16 MB and ~31,000 allocations a CPI.
// Both are read out into dataX/dataY immediately below and not touched
// again, so the later y->clear() cannot be observed through yData.
const std::deque<std::complex<double>> &xData = x->view_data();
const std::deque<std::complex<double>> &yData = y->view_data();

// change deque to std::complex
for (i = 0; i < nSamples; i++)
Expand Down
1 change: 0 additions & 1 deletion src/process/clutter/WienerHopf.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class WienerHopf

/// @brief Deque storage for clutter filter processing.
/// @{
std::deque<std::complex<double>> xData, yData;
/// @}

/// @brief Autocorrelation toeplitz matrix.
Expand Down
Loading
Loading