op25/op25/gr-op25_repeater/lib/ezpwd/ezcod

726 lines
29 KiB
Plaintext

/*
* Ezpwd Reed-Solomon -- Reed-Solomon encoder / decoder library
*
* Copyright (c) 2014, Hard Consulting Corporation.
*
* Ezpwd Reed-Solomon is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. See the LICENSE file at the top of the
* source tree. Ezpwd Reed-Solomon is also available under Commercial license. c++/ezpwd/rs_base
* is redistributed under the terms of the LGPL, regardless of the overall licensing terms.
*
* Ezpwd Reed-Solomon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/
#ifndef _EZPWD_EZCOD
#define _EZPWD_EZCOD
#include <math.h> // M_PI
#include <cmath>
#include <cctype>
#include <cstdint>
#include <ezpwd/rs>
#include <ezpwd/output>
#include <ezpwd/serialize>
//
// EZCOD 3:10 location code w/ Reed-Solomon Error Correction, and average 3m accuracy
//
// - each successive symbol provides greater precision
// - codes nearby each-other are identical in leading characters
// - average 3m precision achieved in 9 symbols
// - more than 4 base-10 digits of precision in both lat and lon after the decimal
// - from 1 to 3 symbols of Reed-Solomon parity
// - 1 parity symbol supplies validation w/ strength equivalent to a check character
// - 2 parity symbols provides correction of 1 lost symbol (no errors)
// - 3 parity symbols provides correction of any 1 error, with verification,
// or recovery of up to any 3 lost symbols (with no other errors)
//
//
// To achieve at least 4 decimal digits of precision after the decimal point, we must have
// defined lat to within 1 part in 1,800,000, and lon to within 1 part in 3,600,000. As each symbol
// supplies bits, we'll refine the computed lat/lon further, reducing the outstanding fraction of
// "parts" yet to be defined.
//
// bits
// symbols latitude longitude
// bits mul parts bits mul parts
// 1 2 4 4 3 8 8
// 2 2 4 16 3 8 64
// 3 3 8 128 2 4 256 // not quite integer lat/lon accuracy
//
// 4 2 4 512 3 8 2,048
// 5 3 8 4,096 2 4 8,192
// 6 2 4 16,384 3 8 65,536
//
// 7 3 8 131,072 2 4 262,144
// 8 2 4 524,288 3 8 2,097,152
// 9 3 8 4,194,304 2 4 8,388,608 parts resolution in 3:10 code
// over [-90, 90] over [-180,180] yields ~3m resolution
//
// vs. 1,800,000 3,600,000 parts resolution in 10:10 code
// over [-90, 90] over [-180,180] yields ~10m resolution
//
// Therefore, within 9 symbols we define lat and lon with better than double the precision of
// 10:10 code's 4 decimal digits after the decimal point. This yields an approximate lineal
// precision of 40,075,000m / 8,388,608 == ~5m in both dimensions at the equator, vs. 40,075,000m /
// 3,600,000 == ~11m for 10:10 codes.
//
// The 10:10 code provides a single check character, which provides about P(1-1/32) certainty
// that the provided code is correct. With EZCOD 3:10/11/12 codes, we provide varying levels of
// detection/correction strength.
//
// - 1 parity symbol: act as a check character (like 10:10 codes), or provide 1 symbol of erasure
// (lost symbol) recovery with no excess parity for validation.
//
// - 2 parity symbols: provide 1 symbol of erasure correction (w/ no other errors) with 1 excess parity
// symbol for validation, or 1 symbol of error detection with no excess parity for validation.
//
// - 3 parity symbols: correct 1 error anywhere w/ 1 excess parity symbol for validation, or up
// to 3 erasures with no excess parity for validation.
//
// Therefore, we'll provide Reed-Solomon RS(31,28-30) error correction (5 bit symbols,
// indicating 31 symbols in the field, and from 1 to 3 roots, therefore up to 28 data symbols in the
// field) over the 9 lat/lon data symbols.
//
//
// MINIMIZING ERROR
//
// Each input lat/lon coordinate will be effectively truncated by the encoding procedure to the
// level of precision (parts) encoded by each symbol. Subsequent symbols then add their (smaller)
// parts to increase precision.
//
// After the last symbol, we know that the actual input coordinates where somewhere
// within the rectangle:
//
// [0,0] -> [0,lon_precision] -> [lat_precision,lon_precision] -> [lat_precision,0]
//
// At first glance, the best way is to perform rounding instead of truncation on ecoding, by
// simply adding 1/2 of the precision. Then, the unmodified output lat/lon decoded represents the
// point nearest actual input coordinate. However, this is NOT ideal. Remember -- the decoding may
// not have access to all the symbols! We want to minimize the error even if only some of the
// symbols are available. Thus, we must apply a correction on decoding.
//
// One way gain rounding instead of truncation on decoding is, after adding the last symbol's
// precision, to add 50% of the value represented by the first bit of the next (missing) symbol's
// precision parts. This would be analogous to receiving the first 2 digits of a 3 digit number:
//
// original: 123
// received: 12_
// range: [120,130)
// guessed: 125 (add 1/2 of the parts represented by the missing digit)
//
// If this is done, then the resulting coordinate would be in the middle of the rectangle of
// possible input lat/lon values that could have resulted in the encoded value. This also works if
// we don't receive and decode all of the symbols; We'll end up with a lat/lon in the middle of the
// (large) rectangle of possible input coordinates.
//
namespace ezpwd {
class ezcod_base {
public:
double latitude; // [-90,+90] angle, degrees
double latitude_error; // total error bar, in meters
double longitude; // [-180,180]
double longitude_error; // total error bar, in meters
double accuracy; // linear accuracy radius, in meters
int confidence; // % parity in excess of last decode
double certainty; // and the associated probability
explicit ezcod_base(
double _lat = 0,
double _lon = 0 )
: latitude( _lat )
, latitude_error( 0 )
, longitude( _lon )
, longitude_error( 0 )
, accuracy( 0 )
, confidence( 100 )
, certainty( 1 )
{
;
}
virtual ~ezcod_base()
{
;
}
typedef std::pair<unsigned char, unsigned char>
symbols_t;
virtual symbols_t symbols()
const
= 0;
virtual std::ostream &output(
std::ostream &lhs )
const
= 0;
virtual std::string encode(
unsigned _preci = 0 ) // override precision
const
= 0;
virtual int decode(
const std::string &_ezcod )
= 0;
};
} // namespace ezpwd
inline std::ostream &operator<<(
std::ostream &lhs,
const ezpwd::ezcod_base
&rhs )
{
return rhs.output( lhs );
}
namespace ezpwd {
//
// ezcod<P,L> -- defaults to 1 PARITY and 9 location symbols (3m) of PRECISION
//
template < unsigned P=1, unsigned L=9 >
class ezcod
: public ezcod_base {
private:
typedef std::array<symbols_t, 12>
bits_t;
static const bits_t bits;
typedef std::array<std::pair<uint32_t, uint32_t>, 12>
parts_t;
static const parts_t parts;
#if defined( DEBUG )
public:
#endif
static const ezpwd::RS<31,31-P>
rscodec;
public:
static constexpr const unsigned PARITY = P; // specified symbols of R-S parity
static constexpr const unsigned PRECISION = L; // default symbols of location precision
static constexpr const unsigned CHUNK = 3; // default chunk size
static constexpr const char SEP_NONE = -1;
static constexpr const char SEP_DEFAULT = 0;
static constexpr const char SEP_DOT = '.';
static constexpr const char SEP_BANG = '!';
static constexpr const char SEP_SPACE = ' ';
static constexpr const char CHK_NONE = -1;
static constexpr const char CHK_DEFAULT = 0;
static constexpr const char CHK_DASH = '-';
static constexpr const char CHK_SPACE = ' ';
unsigned precision;
unsigned chunk; // Location symbol chunk sizes
char separator; // Separator between location and parity symbols
char space; // Fill space between location symbol chunks
//
// ezcod<P,L>() -- supply non-defaults for location precision, chunk size, etc.
//
explicit ezcod(
double _lat = 0,
double _lon = 0,
unsigned _preci = 0,
unsigned _chunk = 0,
char _seper = 0,
char _space = 0 )
: ezcod_base( _lat, _lon )
, precision( _preci ? _preci : PRECISION )
, chunk( _chunk ? _chunk : CHUNK )
, separator( _seper )
, space( _space )
{
if ( P < 1 )
throw std::runtime_error( "ezpwd::ezcod:: At least one parity symbol must be specified" );
if ( precision < 1 || precision > bits.max_size() )
throw std::runtime_error( std::string( "ezpwd::ezcod:: Only 1-" )
+ std::to_string( bits.max_size() )
+ " location symbol may be specified" );
}
explicit ezcod(
const std::string &_ezcod,
unsigned _preci = 0,
unsigned _chunk = 0,
char _seper = 0,
char _space = 0 )
: ezcod( 0, 0, _preci, _chunk, _seper, _space )
{
decode( _ezcod );
}
virtual ~ezcod()
{
;
}
//
// symbols -- return working parity and location precision
//
virtual ezcod_base::symbols_t
symbols()
const
{
return ezcod_base::symbols_t( P, precision );
}
virtual std::ostream &output(
std::ostream &lhs )
const
{
std::streamsize prec = lhs.precision();
std::ios_base::fmtflags
flg = lhs.flags();
lhs.precision( 10 );
std::string uni = "m ";
double acc = accuracy;
double dec = 2;
if ( acc > 1000 ) {
uni = "km";
acc /= 1000;
} else if ( acc < 1 ) {
uni = "mm";
acc *= 1000;
}
if ( acc >= 100 )
dec = 0;
else if ( acc >= 10 )
dec = 1;
lhs << encode( precision )
<< " (" << std::setw( 3 ) << confidence
<< "%) == " << std::showpos << std::fixed << std::setprecision( 10 ) << std::setw( 15 ) << latitude
<< ", " << std::showpos << std::fixed << std::setprecision( 10 ) << std::setw( 15 ) << longitude
<< " +/- " << std::noshowpos << std::fixed << std::setprecision( dec ) << std::setw( 6 ) << acc << uni;
lhs.precision( prec );
lhs.flags( flg );
return lhs;
}
//
// encode() -- encode the lat/lon to 'precision' symbols EZCOD representation
//
virtual std::string encode(
unsigned _preci = 0 ) // override precision
const
{
// Convert lat/lon into a fraction of number of parts assigned to each
double lat_frac= ( latitude + 90 ) / 180;
if ( lat_frac < 0 || lat_frac > 1 )
throw std::runtime_error( "ezpwd::ezcod::encode: Latitude not in range [-90,90]" );
double lon_frac= ( longitude + 180 ) / 360;
if ( lon_frac < 0 || lon_frac > 1 )
throw std::runtime_error( "ezpwd::ezcod::encode: Longitude not in range [-180,180]" );
if ( _preci == 0 )
_preci = precision;
if ( _preci < 1 || _preci > bits.max_size() )
throw std::runtime_error( std::string( "ezpwd::ezcod:: Only 1-" )
+ std::to_string( bits.max_size() )
+ " location symbol may be specified" );
// Compute the integer number of lat/lon parts represented by each coordinate, for the
// specified level of precision, and then truncate to the range [0,..._parts),
// eg. Latitude 90 --> 89.999...
uint32_t lat_parts = parts[_preci-1].first; // [ -90,90 ] / 4,194,304 parts in 9 symbols
uint32_t lon_parts = parts[_preci-1].second; // [-180,180] / 8,388,608 parts ''
uint32_t lat_rem = std::min( lat_parts-1, uint32_t( lat_parts * lat_frac ));
uint32_t lon_rem = std::min( lon_parts-1, uint32_t( lon_parts * lon_frac ));
// Initial loop condition; lat/lon multiplier is left at the base multiplier of the
// previous loop. Then, loop computing the units multiplier, and hten removing the most
// significant bits (multiples of the units multiplier). They will both reach 1
unsigned int lat_mult= lat_parts;
unsigned int lon_mult= lon_parts;
std::string res;
res.reserve( _preci // approximate result length
+ ( chunk && chunk < _preci
? _preci / chunk - 1
: 0 )
+ 1 + P );
for ( auto &b : bits ) {
unsigned char lat_bits= b.first;
unsigned char lon_bits= b.second;
lat_mult >>= lat_bits;
lon_mult >>= lon_bits;
if ( ! lat_mult || ! lon_mult )
break;
// Each set of bits represents the number of times the current multiplier (after
// division by the number of bits we're outputting) would go into the remainder.
// Eg. If _mult was 1024, and _rem is 123 and _bits is 3, we're going to put out
// the next 3 bits of the value 199. The last value ended removing all multiples of
// 1024. So, we first get the new multiplier: 1024 >> 3 == 128. So, we're
// indicating, as a 3-bit value, how many multiples of 128 there are in the value
// 199: 199 / 128 == 1, so the 3-bit value we output is 001
uint32_t lat_val = lat_rem / lat_mult;
lat_rem -= lat_val * lat_mult;
uint32_t lon_val = lon_rem / lon_mult;
lon_rem -= lon_val * lon_mult;
res += char( ( lat_val << lon_bits ) | lon_val );
}
// Add the R-S parity symbols and base-32 encode, add parity separator and chunk
rscodec.encode( res );
serialize::base32::encode( res );
switch( separator ) {
case SEP_NONE:
break;
case SEP_DOT: default:
res.insert( _preci, 1, SEP_DOT );
break;
case SEP_BANG:
case SEP_SPACE:
res.insert( _preci, 1, separator );
break;
}
if ( space != CHK_NONE && chunk && chunk < _preci ) {
for ( unsigned c = _preci / chunk - 1; c > 0; --c ) {
switch ( space ) {
case CHK_NONE:
break;
case CHK_SPACE: default:
res.insert( c * chunk, 1, CHK_SPACE );
break;
case CHK_DASH:
res.insert( c * chunk, 1, space );
break;
}
}
}
return res;
}
//
// deserialize -- Extract base-32, skip whitespace, mark invalid symbols as erasures
// validate -- Remove base-32 encoding, validate and remove parity, returning confidence
// decode -- Attempt to decode a lat/lon, returning the confidence percentage
//
// If data but no parity symbols are supplied, no error checking is performed, and the
// confidence returned will be 0%. No erasures within the supplied data are allowed (as
// there is no capacity to correct them), and an exception will be thrown.
//
// If parity is supplied, then erasures are allowed. So long as the total number of
// erasures is <= the supplied parity symbols, then the decode will proceed (using the
// parity symbols to fill in the erasures), and the returned confidence will reflect the
// amount of unused parity capacity. Each erasure consumes one parity symbol to repair.
//
// We'll allow question-mark or any of the slash characters: "_/\?" to indicate an
// erasure. Either of the "!." symbol may be used to indicates the split between location
// symbols and parity symbols, and must be in a position that corresponds to the indicated
// number of location (this->precision) and parity 'P' symbols. Whitespace symbols and dash
// are ignored: " -".
//
// Thus, an EZCOD like "R3U 1JU QUY!0" may only be decoded by an ezcod<P=1>. Without
// the "!" or ".", it could be an ezcod<P=2> w/precision == 8 -- there's no way to know for
// sure. If no explicit position-parity separator is given, then we assume the default:
// this->precision location symbols, then up to P parity symbols. If additional parity
// symbols are supplied after the separator, then However, an ezcod...<P=3>
//
// If an explicit "!" or "." separator IS provided, then we will attempt to decode the
// position with the given number of position symbols, and up to P parity symbols.
//
// NOTE
//
// Due to a perhaps unexpected feature of R-S codewords, a codeword with MORE parity
// can be successfully decoded by an R-S codec specifying LESS parity symbols. It so
// happens that the data plus (excess) parity + (remaining) parity is STILL a valid codeword
// (so long as the R-S Galois parameters are identical).
//
// Therefore, EZCODs with more parity are accepted by EZCOD parsers configured for less
// parity. Of course, they will have less error/erasure correction strength -- using the
// correctly configured EZCOD codec expecting more R-S parity will maximize the value of all
// the supplied parity.
//
// The full amount of parity (ie. everything after the location/parity separator) is
// discarded in all cases, before the EZCOD location is decoded.
//
private:
unsigned deserialize(
std::string &dec,
std::vector<int> &erasure,
std::vector<char> &invalid )
const
{
serialize::base32::decode( dec, &erasure, &invalid );
// The special symbol '!' or '.' indicates the end of the EZCOD location symbols and the
// beginning of parity: ensure the symbol counts are consistent with the encoding. By
// default the parity symbols begin at offset precision. If we see more than precision
// symbols, we assume that the Lth and subsequent symbols are parity. If a
// location/parity separator is provided, it must be at position this->precision!
// Return offset of start of parity in codeword.
unsigned parbeg = this->PRECISION; // Parity begins after Location, by default
for ( unsigned i = 0; i < invalid.size(); ++i ) {
switch ( invalid[i] ) {
case '!': case '.':
// Remember the offset of the first parity symbol (it'll be in the position of
// the last '!' or '.' symbol we're about to erase), and adjust the indices of
// any erasures following.
parbeg = erasure[i];
dec.erase( parbeg, 1 );
invalid.erase( invalid.begin() + i );
erasure.erase( erasure.begin() + i );
for ( unsigned j = i; j < erasure.size(); ++j )
erasure[j] -= 1;
break;
case '_': case '/': case '\\': case '?':
break;
default:
throw std::runtime_error( std::string( "ezpwd::ezcod::decode: invalid symbol presented: '" )
+ invalid[i] + "'" );
}
}
#if defined( DEBUG ) && DEBUG >= 1
std::cout << " --> 0x" << std::vector<uint8_t>( dec.begin(), dec.begin() + std::min( size_t( parbeg ), dec.length()) )
<< " + 0x" << std::vector<uint8_t>( dec.begin() + std::min( size_t( parbeg ), dec.length()),
dec.begin() + dec.length() )
<< " parity" << std::endl;
#endif
return parbeg;
}
int validate(
std::string &dec )
const
{
// Compute and return validity (which may later be assigned to this->confidence)
int validity = 0; // if no R-S parity provided
#if defined( DEBUG ) && DEBUG >= 1
std::cout << *this << " validate( " << dec << " ) ";
#endif
std::vector<int> erasure;
std::vector<char> invalid;
unsigned parbeg = deserialize( dec, erasure, invalid );
if ( dec.size() > parbeg || erasure.size() > 0 ) {
// Some R-S parity symbol(s) were provided (or erasures were marked). See if we can
// successfully decode/correct, or (at least) use one parity symbol as a check
// character. If we identify more erasures than R-S parity, we must fail; we can't
// recover the data. This will of course be the case if we have *any* erasures in
// the data, and no parity.
unsigned parity = 0;
if ( dec.size() > parbeg )
parity = dec.size() - parbeg;
while ( dec.size() < parbeg + P ) {
erasure.push_back( dec.size() );
dec.resize( dec.size() + 1 );
}
#if defined( DEBUG ) && DEBUG >= 2
std::cout << " --> erasures: " << erasure.size() << " vs. parity: " << parity
<< ": " << std::vector<uint8_t>( dec.begin(), dec.end() ) << std::endl;
#endif
if ( erasure.size() > parity ) {
// We cannot do R-S decoding; not enough parity symbols to even cover erasures.
// If parity symbol(s) were provided ('parity' > 0), and all erasures were due the
// missing remaining parity symbols, we can use the existing parity symbol(s) as
// "check character(s)", by simply re-encoding the supplied non-parity data, and
// see if the generated parity symbol(s) match the supplied parity. This has
// basically the same strength as the 10:10 code's check character.
if ( parity + erasure.size() == P ) {
// All erasures must be at end, in remaining parity symbols!
std::string chk( dec.begin(), dec.begin() + parbeg );
rscodec.encode( chk );
// each parity symbol provided must match the corresponding encoded chk symbol
for ( unsigned i = 0; i < parity; ++i )
if ( dec[parbeg+i] != chk[parbeg+i] )
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; check character mismatch" );
// Check character(s) matched; erasure.size()/P of confidence gone
validity = ezpwd::strength<P>( erasure.size(), erasure, erasure );
} else
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; too many erasures" );
} else {
// We can try R-S decoding; we have (at least) enough parity to try to recover
// any missing symbol(s).
std::vector<int>position;
int corrects= rscodec.decode( dec, erasure, &position );
if ( corrects < 0 )
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; R-S decode failed" );
// Compute confidence, from spare parity capacity. Since R-S decode will not
// return the position of erasures that turn out (by accident) to be correct,
// but they have consumed parity capacity, we re-add them into the correction
// position vector. If the R-S correction reports more corrections than the
// parity can possibly have handled correctly, (eg. 2 reported erasures and an
// unexpected error), then the decode is almost certainly incorrect; fail.
validity = ezpwd::strength<P>( corrects, erasure, position );
if ( validity < 0 )
throw std::runtime_error( "ezpwd::ezcod::decode: Error correction failed; R-S decode overwhelmed" );
}
if ( dec.size() > parbeg )
dec.resize( parbeg ); // Discard any parity symbols
}
return validity;
}
public:
virtual int decode( const std::string &str )
{
// Decode the R-S encoding, computing the confidence. Will raise an exception on any
// error. Don't change this->confidence, this->latitude, ... until there is no longer a
// chance of exception.
std::string decoded( str );
int validity= validate( decoded );
// Unpack the supplied location data; we'll take as much as we are given, up to the
// maximum possible 12 symbols supported (9 symbols yielding ~3m resolution).
uint32_t lat_tot = 0;
uint32_t lon_tot = 0;
uint32_t lat_mult= 1;
uint32_t lon_mult= 1;
auto di = decoded.begin();
for ( auto &b : bits ) {
if ( di == decoded.end() )
break;
unsigned char c = *di++;
unsigned char lat_bits= b.first;
unsigned char lon_bits= b.second;
uint32_t lat_val = c >> lon_bits;
uint32_t lon_val = c & (( 1 << lon_bits ) - 1 );
lat_mult <<= lat_bits;
lat_tot <<= lat_bits;
lat_tot += lat_val;
lon_mult <<= lon_bits;
lon_tot <<= lon_bits;
lon_tot += lon_val;
}
// Convert the sum of lat/lon parts back into degrees, and round the (truncated) value
// to the middle of the error rectangle. This allows us to minimize error even if we
// didn't have access to all of the origin symbols to decode. The absolute error bar as
// a proportional factor [0,1) for lat/lon is at most the scale of the last parts
// multiplier used. We'll use this later to compute the error in meters; for example,
// if the last value we added worked out to be worth units of 25m of the circumference,
// then we must now be within [0,25m) of the original point.
double lat_err = 1.0 / lat_mult;
double lon_err = 1.0 / lon_mult;
latitude = 180 * ( double( lat_tot ) / lat_mult + lat_err / 2 ) - 90;
longitude = 360 * ( double( lon_tot ) / lon_mult + lon_err / 2 ) - 180;
// Remember the decoded location precision for future encoding (overrides the default).
// Compute the certainty probability (0.0,1.0] given the number of parity symbols in
// excess: Given a base-32 symbol: 1 - 1 / ( 32 ^ P ) where P is the number of
// unconsumed parity.
precision = decoded.size();
confidence = validity;
certainty = 0.0;
if ( PARITY * confidence / 100 )
certainty = 1.0 - 1.0 / std::pow( double( 32.0 ),
double( PARITY * confidence / 100 ));
// Compute the resolution error (in m.) of the decoded lat/lon and compute the minimum
// accuracy -- the radius of the circle around the computed latitude/longitude, inside
// which the original latitude/longitude must have been.
//
// original latitude error bar
// \ /
// o -
// | longitude error bar
// | /
// |--x--|
// /|
// / |
// computed -
//
// The maximum distance is the length of the diagonal of the error rectangle defined by
// 1/2 the latitude/longitude error bars.
//
double lon_circ= 1 * M_PI * 6371000;
double lat_circ= 2 * M_PI * 6371000 * std::cos( latitude * M_PI / 180 );
latitude_error = lat_err * lon_circ;
longitude_error = lon_err * lat_circ;
accuracy = sqrt( latitude_error / 2 * latitude_error / 2
+ longitude_error / 2 * longitude_error / 2 );
return confidence;
}
}; // class ezcod
//
// ezcod::rscodec -- Reed-Solomon parity codec
// ezcod::bits -- distribution of lat/lon precision in each code symbol
//
// Quickly establishes an extra bit of precision for Longitude, and then evenly distributes
// future precision between lat/lon, always maintaining extra precision for Longitude.
//
template < unsigned P, unsigned L >
const ezpwd::RS<31,31-P> ezcod<P,L>::rscodec;
// Number of lat/lon bits represented for each location symbol
template < unsigned P, unsigned L >
const typename ezcod<P,L>::bits_t
ezcod<P,L>::bits = {
{
// bits per symbol lat lon
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
// -- --
// 7 8
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
// -- --
// 14 16
ezcod<P,L>::bits_t::value_type( 3, 2 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
// -- --
// 22 23
ezcod<P,L>::bits_t::value_type( 2, 3 ),
ezcod<P,L>::bits_t::value_type( 3, 2 ),
ezcod<P,L>::bits_t::value_type( 2, 3 ),
// -- --
// 29 31
}
};
// Total number of parts that lat/lon is subdivided into, for that number of location symbols.
template < unsigned P, unsigned L >
const typename ezcod<P,L>::parts_t
ezcod<P,L>::parts = {
{
// parts per symbol lat parts lon parts lat lon bits
ezcod<P,L>::parts_t::value_type( 1UL << 2, 1UL << 3 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 4, 1UL << 6 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 7, 1UL << 8 ), // 3, 2
// -- --
// 7 8
ezcod<P,L>::parts_t::value_type( 1UL << 9, 1UL << 11 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 12, 1UL << 13 ), // 3, 2
ezcod<P,L>::parts_t::value_type( 1UL << 14, 1UL << 16 ), // 2, 3
// -- --
// 14 16
ezcod<P,L>::parts_t::value_type( 1UL << 17, 1UL << 18 ), // 3, 2
ezcod<P,L>::parts_t::value_type( 1UL << 19, 1UL << 21 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 22, 1UL << 23 ), // 3, 2
// -- --
// 22 23
ezcod<P,L>::parts_t::value_type( 1UL << 24, 1UL << 26 ), // 2, 3
ezcod<P,L>::parts_t::value_type( 1UL << 27, 1UL << 28 ), // 3, 2
ezcod<P,L>::parts_t::value_type( 1UL << 29, 1UL << 31 ), // 2, 3
// -- --
// 29 31
}
};
} // namespace ezpwd
#endif // _EZPWD_EZCOD