| 
									
										
										
										
											2022-07-20 09:07:00 +02:00
										 |  |  | // Copyright 2021 Rob Riggs <rob@mobilinkd.com>
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | // All rights reserved.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "IirFilter.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <array>
 | 
					
						
							|  |  |  | #include <cstdint>
 | 
					
						
							| 
									
										
										
										
											2022-08-04 15:13:25 +01:00
										 |  |  | #include <cmath>
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | #include <cstddef>
 | 
					
						
							|  |  |  | #include <type_traits>
 | 
					
						
							|  |  |  | #include <tuple>
 | 
					
						
							|  |  |  | #include <limits>
 | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | #include <iostream>
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-10 19:17:56 +02:00
										 |  |  | #include "export.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-04 23:03:07 +02:00
										 |  |  | namespace modemm17 { | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 05:47:48 +02:00
										 |  |  | struct MODEMM17_API Correlator | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-27 17:58:35 +02:00
										 |  |  | 	static const size_t SYMBOLS = 8; | 
					
						
							|  |  |  | 	static const size_t SAMPLES_PER_SYMBOL = 10; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     using buffer_t = std::array<float, SYMBOLS * SAMPLES_PER_SYMBOL>; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     using sync_t = std::array<int8_t, SYMBOLS>; | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     using sample_filter_t = BaseIirFilter<3>; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     buffer_t buffer_; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     float limit_ = 0.; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     size_t symbol_pos_ = 0; | 
					
						
							|  |  |  |     size_t buffer_pos_ = 0; | 
					
						
							|  |  |  |     size_t prev_buffer_pos_ = 0; | 
					
						
							|  |  |  |     int code = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // IIR with Nyquist of 1/240.
 | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     static const std::array<float,3> b; | 
					
						
							|  |  |  |     static const std::array<float,3> a; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     sample_filter_t sample_filter{b, a}; | 
					
						
							|  |  |  |     std::array<int, SYMBOLS> tmp; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     void sample(float value) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |         limit_ = sample_filter(std::abs(value)); | 
					
						
							|  |  |  |         buffer_[buffer_pos_] = value; | 
					
						
							|  |  |  |         prev_buffer_pos_ = buffer_pos_; | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (++buffer_pos_ == buffer_.size()) { | 
					
						
							|  |  |  |             buffer_pos_ = 0; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     float correlate(sync_t sync) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |         float result = 0.; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |         size_t pos = prev_buffer_pos_ + SAMPLES_PER_SYMBOL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (size_t i = 0; i != sync.size(); ++i) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  |             if (pos >= buffer_.size()) { | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |                 pos -= buffer_.size(); // wrapped
 | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |             result += sync[i] * buffer_[pos]; | 
					
						
							|  |  |  |             pos += SAMPLES_PER_SYMBOL; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |         return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     float limit() const {return limit_;} | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     size_t index() const {return prev_buffer_pos_ % SAMPLES_PER_SYMBOL;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * Get the average outer symbol levels at a given index.  This makes trhee | 
					
						
							|  |  |  |      * assumptions. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      *  1. The max symbol value is above 0 and the min symbol value is below 0. | 
					
						
							|  |  |  |      *  2. The samples at the given index only contain outer symbols. | 
					
						
							|  |  |  |      *  3. The index is a peak correlation index. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The first should hold true except for extreme frequency errors.  The | 
					
						
							|  |  |  |      * second holds true for the sync words used for M17.  The third will | 
					
						
							|  |  |  |      * hold true if passed the timing index from a triggered sync word. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |     std::tuple<float, float> outer_symbol_levels(size_t sample_index) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-06-09 20:12:35 +02:00
										 |  |  |         float min_sum = 0; | 
					
						
							|  |  |  |         float max_sum = 0; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |         size_t min_count = 0; | 
					
						
							|  |  |  |         size_t max_count = 0; | 
					
						
							|  |  |  |         size_t index = 0; | 
					
						
							|  |  |  |         for (size_t i = sample_index; i < buffer_.size(); i += SAMPLES_PER_SYMBOL) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             tmp[index++] = buffer_[i] * 1000.; | 
					
						
							|  |  |  |             max_sum += buffer_[i] * ((buffer_[i] > 0.)); | 
					
						
							|  |  |  |             min_sum += buffer_[i] * ((buffer_[i] < 0.)); | 
					
						
							|  |  |  |             max_count += (buffer_[i] > 0.); | 
					
						
							|  |  |  |             min_count += (buffer_[i] < 0.); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return std::make_tuple(min_sum / min_count, max_sum / max_count); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template <typename F> | 
					
						
							|  |  |  |     void apply(F func, uint8_t index) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     	for (size_t i = index; i < buffer_.size(); i += SAMPLES_PER_SYMBOL) | 
					
						
							|  |  |  |     	{ | 
					
						
							|  |  |  |     		func(buffer_[i]); | 
					
						
							|  |  |  |     	} | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct SyncWord | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-27 17:58:35 +02:00
										 |  |  | 	using buffer_t = std::array<int8_t, Correlator::SYMBOLS>; | 
					
						
							|  |  |  | 	using sample_buffer_t = std::array<float, Correlator::SAMPLES_PER_SYMBOL>; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	buffer_t sync_word_; | 
					
						
							|  |  |  | 	sample_buffer_t samples_; | 
					
						
							|  |  |  | 	size_t pos_ = 0; | 
					
						
							|  |  |  | 	size_t timing_index_ = 0; | 
					
						
							|  |  |  | 	bool triggered_ = false; | 
					
						
							|  |  |  | 	int8_t updated_ = 0; | 
					
						
							| 
									
										
										
										
											2022-07-27 17:58:35 +02:00
										 |  |  | 	float magnitude_1_ = 1.0f; | 
					
						
							|  |  |  | 	float magnitude_2_ = -1.0f; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 	SyncWord( | 
					
						
							|  |  |  |         buffer_t&& sync_word, | 
					
						
							| 
									
										
										
										
											2022-07-27 17:58:35 +02:00
										 |  |  |         float magnitude_1, | 
					
						
							|  |  |  |         float magnitude_2 = std::numeric_limits<float>::lowest() | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  |     ) : | 
					
						
							|  |  |  |         sync_word_(std::move(sync_word)), | 
					
						
							|  |  |  |         magnitude_1_(magnitude_1), | 
					
						
							|  |  |  |         magnitude_2_(magnitude_2) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 	{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-27 17:58:35 +02:00
										 |  |  | 	float triggered(Correlator& correlator) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2022-07-27 17:58:35 +02:00
										 |  |  | 		float limit_1 = correlator.limit() * magnitude_1_; | 
					
						
							|  |  |  | 		float limit_2 = correlator.limit() * magnitude_2_; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 		auto value = correlator.correlate(sync_word_); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return (value > limit_1 || value < limit_2) ? value : 0.0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	size_t operator()(Correlator& correlator) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto value = triggered(correlator); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-27 17:58:35 +02:00
										 |  |  | 		float peak_value = 0.0f; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (value != 0) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if (!triggered_) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				samples_.fill(0); | 
					
						
							|  |  |  | 				triggered_ = true; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 			samples_[correlator.index()] = value; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			if (triggered_) | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				// Calculate the timing index on the falling edge.
 | 
					
						
							|  |  |  | 				triggered_ = false; | 
					
						
							|  |  |  | 				timing_index_ = 0; | 
					
						
							|  |  |  | 				peak_value = value; | 
					
						
							|  |  |  | 				uint8_t index = 0; | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             	for (auto f : samples_) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					if (abs(f) > abs(peak_value)) | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						peak_value = f; | 
					
						
							|  |  |  | 						timing_index_ = index; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 					index += 1; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 				updated_ = peak_value > 0 ? 1 : -1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-17 02:25:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 		return timing_index_; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int8_t updated() | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		auto result = updated_; | 
					
						
							|  |  |  | 		updated_ = 0; | 
					
						
							|  |  |  | 		return result; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-04 23:03:07 +02:00
										 |  |  | } // modemm17
 |