| 
									
										
										
										
											2022-07-20 09:07:00 +02:00
										 |  |  | // Copyright 2020 Mobilinkd LLC.
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <array>
 | 
					
						
							|  |  |  | #include <cstdint>
 | 
					
						
							| 
									
										
										
										
											2022-07-16 03:48:33 +02:00
										 |  |  | #include <cmath>
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | #include <stdexcept>
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-04 23:03:07 +02:00
										 |  |  | namespace modemm17 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct LinkSetupFrame | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     using call_t = std::array<char,10>;             // NUL-terminated C-string.
 | 
					
						
							|  |  |  |     using encoded_call_t = std::array<uint8_t, 6>; | 
					
						
							| 
									
										
										
										
											2022-07-16 03:48:33 +02:00
										 |  |  |     using gnss_t = std::array<uint8_t, 14>; | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     using frame_t = std::array<uint8_t, 30>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static constexpr encoded_call_t BROADCAST_ADDRESS = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | 
					
						
							|  |  |  |     static constexpr call_t BROADCAST_CALL = {'B', 'R', 'O', 'A', 'D', 'C', 'A', 'S', 'T', 0}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     enum TxType { PACKET, STREAM }; | 
					
						
							|  |  |  |     enum DataType { DT_RESERVED, DATA, VOICE, MIXED }; | 
					
						
							|  |  |  |     enum EncType { NONE, AES, LFSR, ET_RESERVED }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     call_t tocall_ = {0};   // Destination
 | 
					
						
							|  |  |  |     call_t mycall_ = {0};   // Source
 | 
					
						
							|  |  |  |     TxType  tx_type_ = TxType::STREAM; | 
					
						
							|  |  |  |     DataType data_type_ = DataType::VOICE; | 
					
						
							|  |  |  |     EncType encryption_type_ = EncType::NONE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * The callsign is encoded in base-40 starting with the right-most | 
					
						
							|  |  |  |      * character.  The final value is written out in "big-endian" form, with | 
					
						
							|  |  |  |      * the most-significant value first.  This leads to 0-padding of shorter | 
					
						
							|  |  |  |      * callsigns. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param[in] callsign is the callsign to encode. | 
					
						
							|  |  |  |      * @param[in] strict is a flag (disabled by default) which indicates whether | 
					
						
							|  |  |  |      *  invalid characters are allowed and assugned a value of 0 or not allowed, | 
					
						
							|  |  |  |      *  resulting in an exception. | 
					
						
							|  |  |  |      * @return the encoded callsign as an array of 6 bytes. | 
					
						
							|  |  |  |      * @throw invalid_argument when strict is true and an invalid callsign (one | 
					
						
							|  |  |  |      *  containing an unmappable character) is passed. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     static encoded_call_t encode_callsign(call_t callsign, bool strict = false) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Encode the characters to base-40 digits.
 | 
					
						
							|  |  |  |         uint64_t encoded = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         std::reverse(callsign.begin(), callsign.end()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (auto c : callsign) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             encoded *= 40; | 
					
						
							| 
									
										
										
										
											2022-07-20 09:07:00 +02:00
										 |  |  |             if ((c >= 'A') && (c <= 'Z')) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |             { | 
					
						
							|  |  |  |                 encoded += c - 'A' + 1; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-07-20 09:07:00 +02:00
										 |  |  |             else if ((c >= '0') && (c <= '9')) | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |             { | 
					
						
							|  |  |  |                 encoded += c - '0' + 27; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (c == '-') | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 encoded += 37; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (c == '/') | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 encoded += 38; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (c == '.') | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 encoded += 39; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if (strict) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 throw std::invalid_argument("bad callsign"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const auto p = reinterpret_cast<uint8_t*>(&encoded); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         encoded_call_t result; | 
					
						
							|  |  |  |         std::copy(p, p + 6, result.rbegin()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * Decode a base-40 encoded callsign to its text representation.  This decodes | 
					
						
							|  |  |  |      * a 6-byte big-endian value into a string of up to 9 characters. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     static call_t decode_callsign(encoded_call_t callsign) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         static const char callsign_map[] = "xABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         call_t result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (callsign == BROADCAST_ADDRESS) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             result = BROADCAST_CALL; | 
					
						
							|  |  |  |             return result; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         uint64_t encoded = 0;       // This only works on little endian architectures.
 | 
					
						
							|  |  |  |         auto p = reinterpret_cast<uint8_t*>(&encoded); | 
					
						
							|  |  |  |         std::copy(callsign.rbegin(), callsign.rend(), p); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // decode each base-40 digit and map them to the appriate character.
 | 
					
						
							|  |  |  |         result.fill(0); | 
					
						
							|  |  |  |         size_t index = 0; | 
					
						
							| 
									
										
										
										
											2022-07-20 09:07:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |         while (encoded) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             result[index++] = callsign_map[encoded % 40]; | 
					
						
							|  |  |  |             encoded /= 40; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-16 03:48:33 +02:00
										 |  |  |     static gnss_t encode_gnss(float lat, float lon, float alt) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         gnss_t result; | 
					
						
							|  |  |  |         result.fill(0); | 
					
						
							|  |  |  |         double lat_int, lat_frac; | 
					
						
							|  |  |  |         double lon_int, lon_frac; | 
					
						
							|  |  |  |         uint16_t lat_dec, lon_dec; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         lat_frac = modf(lat, &lat_int); | 
					
						
							|  |  |  |         lon_frac = modf(lon, &lon_int); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bool north = lat_int >= 0; | 
					
						
							|  |  |  |         bool east = lon_int >= 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-20 06:11:14 +02:00
										 |  |  |         result[2] = (int) std::abs(lat_int); | 
					
						
							|  |  |  |         lat_dec = std::abs(lat_frac) * 65536.0f; | 
					
						
							| 
									
										
										
										
											2022-07-16 03:48:33 +02:00
										 |  |  |         result[3] = lat_dec >> 8; | 
					
						
							|  |  |  |         result[4] = lat_dec & 0xFF; | 
					
						
							| 
									
										
										
										
											2022-07-20 06:11:14 +02:00
										 |  |  |         result[5] = (int) std::abs(lon_int); | 
					
						
							|  |  |  |         lon_dec = std::abs(lon_frac) * 65536.0f; | 
					
						
							| 
									
										
										
										
											2022-07-16 03:48:33 +02:00
										 |  |  |         result[6] = lon_dec >> 8; | 
					
						
							|  |  |  |         result[7] = lon_dec & 0xFF; | 
					
						
							|  |  |  |         result[8] = (north ? 0 : 1) | ((east ? 0 : 1)<<1) | (1<<2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         uint16_t alt_enc = (alt * 3.28084f) + 1500; | 
					
						
							|  |  |  |         result[9] = alt_enc >> 8; | 
					
						
							|  |  |  |         result[10] = alt_enc & 0xFF; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static void decode_gnss(const gnss_t& gnss_enc, float& lat, float& lon, float& alt) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         bool north = (gnss_enc[8] & 1) != 0; | 
					
						
							|  |  |  |         bool east = (gnss_enc[8] & 2) != 0; | 
					
						
							|  |  |  |         uint32_t lat_int = gnss_enc[2]; | 
					
						
							|  |  |  |         uint16_t lat_frac = (gnss_enc[3] << 8) + gnss_enc[4]; | 
					
						
							|  |  |  |         uint32_t lon_int = gnss_enc[5]; | 
					
						
							|  |  |  |         uint16_t lon_frac = (gnss_enc[6] << 8) + gnss_enc[7]; | 
					
						
							|  |  |  |         lat = lat_int + (lat_frac / 65536.0f); | 
					
						
							|  |  |  |         lat = north ? lat : -lat; | 
					
						
							|  |  |  |         lon = lon_int + (lon_frac / 65536.0f); | 
					
						
							|  |  |  |         lat = east ? lon : -lon; | 
					
						
							|  |  |  |         uint16_t alt_enc = (gnss_enc[9] << 8) + gnss_enc[10]; | 
					
						
							|  |  |  |         alt = (alt_enc - 1500) / 3.28084f; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 03:22:18 +02:00
										 |  |  |     LinkSetupFrame() | 
					
						
							|  |  |  |     {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     LinkSetupFrame& myCall(const char*) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return *this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-04 23:03:07 +02:00
										 |  |  | } // modemm17
 |