mirror of https://github.com/lwvmobile/dsd-fme.git
688 lines
27 KiB
C
688 lines
27 KiB
C
/*-------------------------------------------------------------------------------
|
||
* dsd_gps.c
|
||
* GPS Handling Functions for Various Protocols
|
||
*
|
||
* LWVMOBILE
|
||
* 2023-12 DSD-FME Florida Man Edition
|
||
*-----------------------------------------------------------------------------*/
|
||
|
||
#include "dsd.h"
|
||
|
||
void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input)
|
||
{
|
||
//NOTE: This is defined in ETSI TS 102 361-4 V1.12.1 (2023-07) p208
|
||
//6.6.11.3.2 USBD Polling Service Poll Response PDU for LIP (That's a mouthful)
|
||
|
||
int slot = state->currentslot;
|
||
|
||
//NOTE: This format is pretty much the same as DMR EMB GPS, but has a few extra elements,
|
||
//so I got lazy and just lifted most of the code from there, also assuming same lat/lon calcs
|
||
//since those have been tested to work in DMR EMB GPS and units are same in LIP
|
||
|
||
fprintf (stderr, "Location Information Protocol; ");
|
||
|
||
// uint8_t service_type = (uint8_t)ConvertBitIntoBytes(&input[0], 4); //checked before arrival here
|
||
uint8_t time_elapsed = (uint8_t)ConvertBitIntoBytes(&input[6], 2);
|
||
uint8_t lon_sign = input[8];
|
||
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&input[9], 24); //8, 25
|
||
uint8_t lat_sign = input[33];
|
||
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&input[34], 23); //33, 24
|
||
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&input[57], 2);
|
||
uint8_t hor_vel = (uint8_t)ConvertBitIntoBytes(&input[59], 7);
|
||
uint8_t dir_tra = (uint8_t)ConvertBitIntoBytes(&input[66], 4);
|
||
uint8_t reason = (uint8_t)ConvertBitIntoBytes(&input[70], 3);
|
||
uint8_t add_hash = (uint8_t)ConvertBitIntoBytes(&input[73], 8); //MS Source Address Hash
|
||
|
||
//NOTE: May need to use double instead of float to avoid rounding errors
|
||
double latitude, longitude = 0.0f;
|
||
double lat_unit = (double)180/ pow (2.0, 24); //180 divided by 2^24 -- 6.3.30
|
||
double lon_unit = (double)360/ pow (2.0, 25); //360 divided by 2^25 -- 6.3.50
|
||
double lon_sf = 1.0f; //float value we can multiple longitude with
|
||
double lat_sf = 1.0f; //float value we can multiple latitude with
|
||
|
||
char latstr[3];
|
||
char lonstr[3];
|
||
sprintf (latstr, "%s", "N");
|
||
sprintf (lonstr, "%s", "E");
|
||
|
||
char deg_glyph[4];
|
||
sprintf (deg_glyph, "%s", "°");
|
||
|
||
//lat and lon calculations
|
||
if (lat_sign)
|
||
{
|
||
lat = 0x800001 - lat;
|
||
sprintf (latstr, "%s", "S");
|
||
lat_sf = -1.0f;
|
||
}
|
||
latitude = ((double)lat * lat_unit);
|
||
|
||
if (lon_sign)
|
||
{
|
||
lon = 0x1000001 - lon;
|
||
sprintf (lonstr, "%s", "W");
|
||
lon_sf = -1.0f;
|
||
}
|
||
longitude = ((double)lon * lon_unit);
|
||
|
||
//6.3.63 Position Error
|
||
//6.3.17 Horizontal velocity
|
||
/*
|
||
Horizontal velocity shall be encoded for speeds 0 km/h to 28 km/h in 1 km/h steps and
|
||
from 28 km/h onwards using equation: v = C × (1 + x)^(K-A) + B where:
|
||
*/
|
||
float v = 0.0f;
|
||
float C = 16.0f;
|
||
float x = 0.038f;
|
||
float A = 13.0f;
|
||
float K = (float)hor_vel;
|
||
float B = 0.0f;
|
||
|
||
if (hor_vel > 28)
|
||
v = C * (pow(1+x, K-A)) + B; //I think this pow function is set up correctly
|
||
else v = (float)hor_vel;
|
||
|
||
float dir = (((float)dir_tra + 11.25f)/22.5f); //page 68, Table 6.45
|
||
|
||
//truncated and rounded forms
|
||
int vt = (int)v;
|
||
int dt = (int)dir;
|
||
|
||
//sanity check
|
||
if (abs (latitude) < 90 && abs(longitude) < 180)
|
||
{
|
||
fprintf (stderr, "Src(Hash); %03d; Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf); Spd: %d km/h; Dir: %d%s",add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude, vt, dt, deg_glyph);
|
||
|
||
//6.3.63 Position Error
|
||
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
|
||
if (pos_err == 0x7 ) fprintf (stderr, "\n Position Error: Unknown or Invalid;");
|
||
else fprintf (stderr, "\n Position Error: Less than %dm;", position_error);
|
||
|
||
//Reason For Sending 6.6.11.3.3 Table 6.80
|
||
if (reason) fprintf (stderr, " Reserved: %d;", reason);
|
||
else fprintf (stderr, " Request Response; ");
|
||
|
||
//6.3.78 Time elapsed
|
||
if (time_elapsed == 0) fprintf (stderr, " TE: < 5s;");
|
||
if (time_elapsed == 1) fprintf (stderr, " TE: < 5m;");
|
||
if (time_elapsed == 2) fprintf (stderr, " TE: < 30m;");
|
||
if (time_elapsed == 3) fprintf (stderr, " TE: NA or UNK;"); //not applicable or unknown
|
||
|
||
//save to array for ncurses
|
||
if (pos_err != 0x7)
|
||
{
|
||
sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s; Err: %dm; Spd: %d km/h; Dir: %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, position_error, vt, dt, deg_glyph);
|
||
}
|
||
else sprintf (state->dmr_embedded_gps[slot], "%03d; LIP: %.5lf%s%s %.5lf%s%s Unknown Pos Err; Spd: %d km/h; Dir %d%s", add_hash, latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, vt, dt, deg_glyph);
|
||
|
||
//save to LRRP report for mapping/logging
|
||
FILE * pFile; //file pointer
|
||
if (opts->lrrp_file_output == 1)
|
||
{
|
||
//open file by name that is supplied in the ncurses terminal, or cli
|
||
pFile = fopen (opts->lrrp_out_file, "a");
|
||
fprintf (pFile, "%s\t", getDateL() );
|
||
fprintf (pFile, "%s\t", getTimeL() );
|
||
fprintf (pFile, "%08d\t", add_hash);
|
||
fprintf (pFile, "%.5lf\t", latitude);
|
||
fprintf (pFile, "%.5lf\t", longitude);
|
||
fprintf (pFile, "%d\t", vt); //speed in km/h
|
||
fprintf (pFile, "%d\t", dt); //direction of travel
|
||
fprintf (pFile, "\n");
|
||
fclose (pFile);
|
||
|
||
}
|
||
|
||
}
|
||
else fprintf (stderr, " Position Calculation Error;");
|
||
|
||
}
|
||
|
||
void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int type)
|
||
{
|
||
int slot = state->currentslot;
|
||
|
||
//NOTE: The Only difference between Short (type == 1) and Long Format (type == 2)
|
||
//is the utc_ss3 on short vs utc_ss6 and inclusion of COG value on long
|
||
|
||
//NOTE: MFID Specific Formats are not handled here, unknown, could be added if worked out
|
||
//they could share most of the same elements and use the large spare bits in block 2 for extra
|
||
// uint8_t mfid = (uint8_t)ConvertBitIntoBytes(&input[88], 8); //on type 3, last octet of first block carries MFID
|
||
|
||
// uint8_t nmea_c = input[0]; //encrypted -- checked before we get here
|
||
uint8_t nmea_ns = input[1]; //north/south (lat sign)
|
||
uint8_t nmea_ew = input[2]; //east/west (lon sign)
|
||
uint8_t nmea_q = input[3]; //Quality Indicator (no fix or fix valid)
|
||
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[4], 7); //speed in knots (127 = greater than 126 knots)
|
||
|
||
//Latitude Bits
|
||
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[11], 7); //Latitude Degrees
|
||
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[18], 6); //Latitude Minutes
|
||
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[24], 14); //Latitude Fractions of Minutes
|
||
|
||
//Longitude Bits
|
||
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[38], 8); //Longitude Degrees
|
||
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[46], 6); //Longitude Minutes
|
||
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[52], 14); //Longitude Fractions of Minutes
|
||
|
||
//UTC Time and COG
|
||
uint8_t nmea_utc_hh = (uint8_t)ConvertBitIntoBytes(&input[66], 5);
|
||
uint8_t nmea_utc_mm = (uint8_t)ConvertBitIntoBytes(&input[71], 6);
|
||
//seconds and the addition of COG is the difference between short and long formats
|
||
uint8_t nmea_utc_ss3 = (uint8_t)ConvertBitIntoBytes(&input[77], 3); //seconds in 10s
|
||
uint8_t nmea_utc_ss6 = (uint8_t)ConvertBitIntoBytes(&input[77], 6); //seconds in 1s
|
||
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[103], 9); //course over ground in degrees
|
||
|
||
//lat and lon conversion
|
||
char deg_glyph[4];
|
||
sprintf (deg_glyph, "%s", "°");
|
||
float latitude = 0.0f;
|
||
float longitude = 0.0f;
|
||
// float m_unit = 1.0f / 60.0f; //unit to convert min into decimal value - (1/60)*60 minutes = 1 degree
|
||
// float mm_unit = 1.0f / 10000.0f; //unit to convert minf into decimal value - (0000 - 9999) 0.0001×9999 = .9999 minutes, so its sub 1 minute decimal
|
||
|
||
//testing w/ Harris NMEA like values (ran tests over there with this code, this seems to work on those values)
|
||
float m_unit = 1.0f / 60.0f; //unit to convert min into decimal value - (1/60)*60 minutes = 1 degree
|
||
float mm_unit = 1.0f / 600000.0f; //unit to convert minf into decimal value - (0000 - 9999) 0.0001×9999 = .9999 minutes, so its sub 1 minute decimal
|
||
|
||
//speed conversion
|
||
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
|
||
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
|
||
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
|
||
fkph = (float)nmea_speed * 1.852f;
|
||
|
||
//calculate decimal representation of latidude and longitude (need some samples to test)
|
||
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
|
||
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
|
||
|
||
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
|
||
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
|
||
|
||
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
|
||
|
||
//Speed in Knots
|
||
if (nmea_speed > 126)
|
||
fprintf (stderr, " SPD > 126 knots or %f kph;", fkph);
|
||
else
|
||
fprintf (stderr, " SPD: %d knots; %f kph;", nmea_speed, fkph);
|
||
|
||
//Print UTC Time and COG according to Format Type
|
||
if (type == 1)
|
||
fprintf (stderr, " FIX: %d; %02d:%02d:%02d UTC; Short Format;", nmea_q, nmea_utc_hh, nmea_utc_mm, nmea_utc_ss3);
|
||
if (type == 2)
|
||
fprintf (stderr, " FIX: %d; %02d:%02d:%02d UTC; COG: %d%s; Long Format;", nmea_q, nmea_utc_hh, nmea_utc_mm, nmea_utc_ss6, nmea_cog, deg_glyph);
|
||
|
||
//save to ncurses string
|
||
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
|
||
|
||
//save to LRRP report for mapping/logging
|
||
FILE * pFile; //file pointer
|
||
if (opts->lrrp_file_output == 1)
|
||
{
|
||
int s = (int)fkph; //rounded interger format for the log report
|
||
int a = 0;
|
||
if (type == 2)
|
||
a = nmea_cog; //long format only
|
||
//open file by name that is supplied in the ncurses terminal, or cli
|
||
pFile = fopen (opts->lrrp_out_file, "a");
|
||
fprintf (pFile, "%s\t", getDateL() );
|
||
fprintf (pFile, "%s\t", getTimeL() ); //could switch to UTC time if desired, but would require local user offset
|
||
fprintf (pFile, "%08d\t", src);
|
||
fprintf (pFile, "%.6lf\t", latitude);
|
||
fprintf (pFile, "%.6lf\t", longitude);
|
||
fprintf (pFile, "%d\t ", s);
|
||
fprintf (pFile, "%d\t ", a);
|
||
fprintf (pFile, "\n");
|
||
fclose (pFile);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//restructured the Harris GPS to flow more like the DMR UDT NMEA Format when possible
|
||
void nmea_harris (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src, int slot)
|
||
{
|
||
|
||
//GPS working, Time Working (old method), COG and Speed are logical on due west sample
|
||
//TODO: Fiddle/Test various ns and ew bits for proper signs in case its located elsewhere
|
||
//UPDATE: Found two contiguous bits that always work on all my accumulated samples
|
||
//the bits also place it with the 'lone' flagged bit observed prior
|
||
|
||
uint8_t nmea_ns = input[88]; //north/south (lat sign) //72 previously used bit
|
||
uint8_t nmea_ew = input[89]; //east/west (lon sign) //88 previously used bit
|
||
uint8_t nmea_q = input[147]; //Quality Indicator (last fix or current fix)
|
||
// uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 7); //129,7
|
||
uint8_t nmea_speed = (uint8_t)ConvertBitIntoBytes(&input[129], 6); //this is one bit shorter than called for, but I believe it works
|
||
|
||
//Latitude Bits
|
||
uint8_t nmea_ndeg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //Latitude Degrees
|
||
uint8_t nmea_nmin = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //Latitude Minutes
|
||
uint16_t nmea_nminf = (uint16_t)ConvertBitIntoBytes(&input[42], 14); //Latitude Fractions of Minutes
|
||
|
||
//Longitude Bits
|
||
uint8_t nmea_edeg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //Longitude Degrees
|
||
uint8_t nmea_emin = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //Longitude Minutes
|
||
uint16_t nmea_eminf = (uint16_t)ConvertBitIntoBytes(&input[74], 14); //Longitude Fractions of Minutes
|
||
|
||
//NOTE: Removed UTC hh.mm.ss6 format found in DMR UDT, could not make it work
|
||
//with bits already established for time and it works extremely well as is
|
||
|
||
//Course Over Ground in Degrees (0-360)
|
||
uint16_t nmea_cog = (uint16_t)ConvertBitIntoBytes(&input[135], 9) % 360; //course over ground in degrees //working on due west sample without %360
|
||
|
||
//timestamp (16-bit value w/ appended 17th bit to MSB)
|
||
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16);
|
||
rtime |= input[144] << 16; //this bit is flagged on after 0xFFFF rollover in the late afternoon
|
||
uint32_t thour = rtime / 3600;
|
||
uint32_t tmin = (rtime % 3600) / 60;
|
||
uint32_t tsec = (rtime % 3600) % 60;
|
||
|
||
//lat and lon conversion
|
||
char deg_glyph[4];
|
||
sprintf (deg_glyph, "%s", "°");
|
||
float latitude = 0.0f;
|
||
float longitude = 0.0f;
|
||
float m_unit = 1.0f / 60.0f;
|
||
float mm_unit = 1.0f / 600000.0f;
|
||
|
||
//speed conversion
|
||
float fmps, fmph, fkph = 0.0f; //conversion of knots to mps, kph, and mph values
|
||
fmps = (float)nmea_speed * 0.514444; UNUSED(fmps);
|
||
fmph = (float)nmea_speed * 1.15078f; UNUSED(fmph);
|
||
fkph = (float)nmea_speed * 1.852f; UNUSED(fkph);
|
||
|
||
//calculate decimal representation of latidude and longitude (need some samples to test)
|
||
latitude = ( (float)nmea_ndeg + ((float)nmea_nmin*m_unit) + ((float)nmea_nminf*mm_unit) );
|
||
longitude = ( (float)nmea_edeg + ((float)nmea_emin*m_unit) + ((float)nmea_eminf*mm_unit) );
|
||
|
||
//This is opposite (seemingly) compared to the version specified by DMR UDT NMEA, could still be different bits involved (to be tested)
|
||
// if (nmea_ns) latitude *= -1.0f; //1 is South, 0 is North
|
||
// if (nmea_ew) longitude *= -1.0f; //1 is West, 0 is East
|
||
|
||
//DMR UDT NMEA original version (working now with new bit selection 88 and 89 like how DMR UDT NMEA format specifies)
|
||
if (!nmea_ns) latitude *= -1.0f; //0 is South, 1 is North
|
||
if (!nmea_ew) longitude *= -1.0f; //0 is West, 1 is East
|
||
|
||
fprintf (stderr, "\n");
|
||
|
||
fprintf (stderr, " VCH: %d - SRC: %08d;", slot, src);
|
||
fprintf (stderr, " GPS: %f%s, %f%s;", latitude, deg_glyph, longitude, deg_glyph);
|
||
|
||
//Speed in Knots (assuming in knots, and not in mps, or kph)
|
||
if (nmea_speed > 126)
|
||
fprintf (stderr, " SPD > 126 knots or %f MPH;", fmph); //using MPH here, its Harris, they're in the U.S.
|
||
else
|
||
fprintf (stderr, " SPD: %d knots; %f MPH;", nmea_speed, fmph);
|
||
|
||
//Course Over Ground (COG)
|
||
fprintf (stderr, " COG: %03d%s;", nmea_cog, deg_glyph); //%360
|
||
|
||
//debug speed
|
||
// fprintf (stderr, " RSPD: %02X;", nmea_speed);
|
||
|
||
//debug cog
|
||
// fprintf (stderr, " RCOG: %03X;", nmea_cog >> 0);
|
||
|
||
//UTC Time
|
||
fprintf (stderr, " T: %02d:%02d:%02d UTC;", thour, tmin, tsec);
|
||
|
||
//Fix Indicator
|
||
if (nmea_q) fprintf (stderr, " Current Fix;");
|
||
else fprintf (stderr, " Last Fix;");
|
||
|
||
//save to ncurses string
|
||
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", latitude, deg_glyph, longitude, deg_glyph);
|
||
|
||
//save to LRRP report for mapping/logging
|
||
FILE * pFile; //file pointer
|
||
if (opts->lrrp_file_output == 1)
|
||
{
|
||
//rounded interger formats for the log report
|
||
int s = (int)fkph;
|
||
int a = nmea_cog;
|
||
|
||
//open file by name that is supplied in the ncurses terminal, or cli
|
||
pFile = fopen (opts->lrrp_out_file, "a");
|
||
fprintf (pFile, "%s\t", getDateL() );
|
||
fprintf (pFile, "%s\t", getTimeL() ); //could switch to UTC time on PDU if desired
|
||
fprintf (pFile, "%08d\t", src);
|
||
fprintf (pFile, "%.6lf\t", latitude);
|
||
fprintf (pFile, "%.6lf\t", longitude);
|
||
fprintf (pFile, "%d\t ", s);
|
||
fprintf (pFile, "%d\t ", a);
|
||
fprintf (pFile, "\n");
|
||
fclose (pFile);
|
||
|
||
}
|
||
|
||
//NOTE: There seems to be a few more octets left undecoded in this PDU
|
||
//not including the CRC obviously, unsure of their meaning, could relate
|
||
//to other GPS functions like precision, etc, unknown.
|
||
|
||
//NOTE2: All these values are just best effort based on observation and
|
||
//making map points match using logic and distance over time for speed and course
|
||
//without any documentation, any of these values may still be wrong
|
||
|
||
}
|
||
|
||
//fallback version (if desired/required)
|
||
void harris_gps(dsd_opts * opts, dsd_state * state, int slot, uint8_t * input)
|
||
{
|
||
|
||
uint8_t lat_sign, lat_deg, lat_min = 0;
|
||
uint8_t lon_sign, lon_deg, lon_min = 0;
|
||
uint16_t lat_mmin = 0;
|
||
uint16_t lon_mmin = 0;
|
||
|
||
//potentially in this PDU, but unverifiable without documentation or reasonable
|
||
//mathematical proof vs map points and direction of travel / distance over time (assuming the radio is facing that direction)
|
||
uint16_t rspeed = (uint16_t)ConvertBitIntoBytes(&input[136], 8); //MSB //136
|
||
rspeed = (rspeed << 8) + (uint16_t)ConvertBitIntoBytes(&input[128], 8); //LSB //128
|
||
|
||
//this works okay for example of driver driving due west at the speed limit of the road (fluke?)
|
||
uint16_t rangle = (uint16_t)ConvertBitIntoBytes(&input[120], 8); //120,8
|
||
float fspeed = (float)rspeed;
|
||
float fangle = (float)rangle;
|
||
|
||
fspeed /= 255.0f; //unit value of 1 bit
|
||
fspeed *= 1.56f; //this is based on an observation of a driver moving approx 210 meters in about 8 seconds and this makes it just under the speed limit on the road there
|
||
// fangle *= 360.0f/255.0f; //unit value of 1 bit
|
||
fangle *= 2.0f; //may exceed 360 degrees as is (use %)
|
||
|
||
int s, a = 0;
|
||
s = (int)fspeed * 3.6;
|
||
a = (int)fangle % 360;
|
||
|
||
//fix and quality indicators?
|
||
uint8_t fix = input[147]; UNUSED(fix);
|
||
uint8_t quality = (uint16_t)ConvertBitIntoBytes(&input[148], 3); UNUSED(quality);
|
||
|
||
//timestamp (16-bit value w/ appended 17th bit to MSB)
|
||
uint32_t rtime = (uint32_t)ConvertBitIntoBytes(&input[104], 16); //seconds since midnight since last GPS fix (whatever the radio has as internal time)
|
||
rtime |= input[144] << 16; //test appending this as bit 17 (observed this bit set after the afternoon 0xFFFF rollover)
|
||
uint32_t thour = rtime / 3600;
|
||
uint32_t tmin = (rtime % 3600) / 60;
|
||
uint32_t tsec = (rtime % 3600) % 60;
|
||
|
||
float lat_dec = 0.0f;
|
||
float lon_dec = 0.0f;
|
||
|
||
char deg_glyph[4];
|
||
sprintf (deg_glyph, "%s", "°");
|
||
|
||
//This appears to be similar to the NMEA GPGGA format (DDmm.mm) but
|
||
//octets are ordered in least significant to most significant value
|
||
lat_mmin = (uint16_t)ConvertBitIntoBytes(&input[40], 16); //? bits required, but grabbing two octets
|
||
lat_min = (uint8_t)ConvertBitIntoBytes(&input[58], 6); //6 bits required to get 60
|
||
lat_deg = (uint8_t)ConvertBitIntoBytes(&input[65], 7); //7 bits required to get 90
|
||
lat_sign = input[72]; //64
|
||
|
||
lon_mmin = (uint16_t)ConvertBitIntoBytes(&input[72], 16); //? bits required, but grabbing two octets
|
||
lon_min = (uint8_t)ConvertBitIntoBytes(&input[90], 6); //6 bits required to get 60
|
||
lon_deg = (uint8_t)ConvertBitIntoBytes(&input[96], 8); //8 bits required to get 180
|
||
lon_sign = input[88]; //88, unsure of a correct location, but on the sample with 0 minutes, this lonely bit was flagged on
|
||
|
||
int src = 0;
|
||
if (slot == 0) src = state->lastsrc;
|
||
if (slot == 1) src = state->lastsrcR;
|
||
|
||
//calculate decimal representation (was a pain to figure out the sub minute values)
|
||
lat_dec = ( (float)lat_deg + ((float)lat_min/60.0f) + ((float)lat_mmin/600000.0f) );
|
||
lon_dec = ( (float)lon_deg + ((float)lon_min/60.0f) + ((float)lon_mmin/600000.0f) );
|
||
|
||
if (lat_sign)
|
||
lat_dec *= -1.0f;
|
||
|
||
if (lon_sign)
|
||
lon_dec *= -1.0f;
|
||
|
||
//line break
|
||
fprintf (stderr, "\n");
|
||
fprintf (stderr, " GPS: %f%s, %f%s;", lat_dec, deg_glyph, lon_dec, deg_glyph);
|
||
|
||
//gps fix time
|
||
// fprintf (stderr, " RT: %05X;", rtime); //debug
|
||
fprintf (stderr, " LTS: %02d:%02d:%02d UTC;", thour, tmin, tsec); //last time synced to GPS
|
||
|
||
//speed and direction
|
||
// fprintf (stderr, " RSPD: %04X;", rspeed); //debug
|
||
// fprintf (stderr, " RANG: %02X;", rangle); //debug
|
||
// fprintf (stderr, " MPS: %06.02f;", fspeed);
|
||
// fprintf (stderr, " KPH: %06.02f;", fspeed * 3.6f);
|
||
// fprintf (stderr, " MPH: %06.02f;", fspeed * 2.2369f);
|
||
fprintf (stderr, " DIR: %03d%s;", a, deg_glyph);
|
||
|
||
// fprintf (stderr, " FIX: %d", fix);
|
||
// fprintf (stderr, " QLT: %02d", quality);
|
||
|
||
// fprintf (stderr, " SRC: %08d;", src);
|
||
|
||
//save to array for ncurses
|
||
sprintf (state->dmr_embedded_gps[slot], "GPS: (%f%s, %f%s)", lat_dec, deg_glyph, lon_dec, deg_glyph);
|
||
|
||
//save to LRRP report for mapping/logging
|
||
FILE * pFile; //file pointer
|
||
if (opts->lrrp_file_output == 1)
|
||
{
|
||
//open file by name that is supplied in the ncurses terminal, or cli
|
||
pFile = fopen (opts->lrrp_out_file, "a");
|
||
fprintf (pFile, "%s\t", getDateL() );
|
||
fprintf (pFile, "%s\t", getTimeL() );
|
||
fprintf (pFile, "%08d\t", src);
|
||
fprintf (pFile, "%.6lf\t", lat_dec);
|
||
fprintf (pFile, "%.6lf\t", lon_dec);
|
||
fprintf (pFile, "%d\t ", s);
|
||
fprintf (pFile, "%d\t ", a);
|
||
fprintf (pFile, "\n");
|
||
fclose (pFile);
|
||
|
||
}
|
||
|
||
//NOTE: Thanks to DSheirer (SDRTrunk) for helping me work out a few of the things in here
|
||
//not entirely convinced on some of these calcs (speed, angle, and TS) but sure these bits
|
||
//are actual speed/direction/timestamps judging from what the coordinates show on a map
|
||
}
|
||
|
||
//externalize embedded GPS - Confirmed working now on NE, NW, SE, and SW coordinates
|
||
void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||
{
|
||
UNUSED(opts);
|
||
|
||
fprintf (stderr, "%s", KYEL);
|
||
fprintf (stderr, " Embedded GPS:");
|
||
uint8_t slot = state->currentslot;
|
||
uint8_t pf = lc_bits[0];
|
||
uint8_t res_a = lc_bits[1];
|
||
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 4);
|
||
uint8_t pos_err = (uint8_t)ConvertBitIntoBytes(&lc_bits[20], 3);
|
||
UNUSED2(res_a, res_b);
|
||
|
||
char deg_glyph[4];
|
||
sprintf (deg_glyph, "%s", "°");
|
||
|
||
uint32_t lon_sign = lc_bits[23];
|
||
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[24], 24);
|
||
uint32_t lat_sign = lc_bits[48];
|
||
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
|
||
double lon_sf = 1.0f; //float value we can multiple longitude with
|
||
double lat_sf = 1.0f; //float value we can multiple latitude with
|
||
|
||
double lat_unit = (double)180/ pow (2.0, 24); //180 divided by 2^24
|
||
double lon_unit = (double)360/ pow (2.0, 25); //360 divided by 2^25
|
||
|
||
char latstr[3];
|
||
char lonstr[3];
|
||
sprintf (latstr, "%s", "N");
|
||
sprintf (lonstr, "%s", "E");
|
||
|
||
//run calculations and print
|
||
//7.2.16 and 7.2.17 (two's compliment)
|
||
|
||
double latitude = 0;
|
||
double longitude = 0;
|
||
|
||
if (pf) fprintf (stderr, " Protected");
|
||
else
|
||
{
|
||
if (lat_sign)
|
||
{
|
||
lat = 0x800001 - lat;
|
||
sprintf (latstr, "%s", "S");
|
||
lat_sf = -1.0f;
|
||
}
|
||
latitude = ((double)lat * lat_unit);
|
||
|
||
if (lon_sign)
|
||
{
|
||
lon = 0x1000001 - lon;
|
||
sprintf (lonstr, "%s", "W");
|
||
lon_sf = -1.0f;
|
||
}
|
||
longitude = ((double)lon * lon_unit);
|
||
|
||
//sanity check
|
||
if (abs (latitude) < 90 && abs(longitude) < 180)
|
||
{
|
||
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf)", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, lat_sf * latitude, lon_sf * longitude);
|
||
|
||
//7.2.15 Position Error
|
||
uint16_t position_error = 2 * pow(10, pos_err); //2 * 10^pos_err
|
||
if (pos_err == 0x7 ) fprintf (stderr, "\n Position Error: Unknown or Invalid");
|
||
else fprintf (stderr, "\n Position Error: Less than %dm", position_error);
|
||
|
||
//save to array for ncurses
|
||
if (pos_err != 0x7)
|
||
{
|
||
sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Err: %dm", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, position_error);
|
||
}
|
||
else sprintf (state->dmr_embedded_gps[slot], "GPS: %.5lf%s%s %.5lf%s%s Unknown Pos Err", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr);
|
||
|
||
//save to LRRP report for mapping/logging
|
||
FILE * pFile; //file pointer
|
||
if (opts->lrrp_file_output == 1)
|
||
{
|
||
int src = 0;
|
||
if (slot == 0) src = state->lasttg;
|
||
if (slot == 1) src = state->lasttgR;
|
||
|
||
//open file by name that is supplied in the ncurses terminal, or cli
|
||
pFile = fopen (opts->lrrp_out_file, "a");
|
||
fprintf (pFile, "%s\t", getDateL() );
|
||
fprintf (pFile, "%s\t", getTimeL() );
|
||
fprintf (pFile, "%08d\t", src);
|
||
fprintf (pFile, "%.5lf\t", latitude);
|
||
fprintf (pFile, "%.5lf\t", longitude);
|
||
fprintf (pFile, "0\t " ); //zero for velocity
|
||
fprintf (pFile, "0\t " ); //zero for azimuth
|
||
fprintf (pFile, "\n");
|
||
fclose (pFile);
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
fprintf (stderr, "%s", KNRM);
|
||
}
|
||
|
||
//This Function has been redone and variables fixed to mirror SDRTrunk
|
||
//my earlier assumption that this was the same format as DMR Embedded GPS was incorrect
|
||
void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[])
|
||
{
|
||
|
||
fprintf (stderr, "%s", KYEL);
|
||
fprintf (stderr, " GPS:");
|
||
uint8_t slot = state->currentslot;
|
||
uint8_t pf = lc_bits[0];
|
||
uint8_t res_a = lc_bits[1];
|
||
uint8_t res_b = (uint8_t)ConvertBitIntoBytes(&lc_bits[16], 7);
|
||
uint8_t expired = lc_bits[23]; //this bit seems to indicate that the GPS coordinates are out of date or fresh
|
||
|
||
char deg_glyph[4];
|
||
sprintf (deg_glyph, "%s", "°");
|
||
|
||
uint32_t lon_sign = lc_bits[48];
|
||
uint32_t lon = (uint32_t)ConvertBitIntoBytes(&lc_bits[49], 23);
|
||
uint32_t lat_sign = lc_bits[24];
|
||
uint32_t lat = (uint32_t)ConvertBitIntoBytes(&lc_bits[25], 23);
|
||
//todo: acquire more samples for additional validation
|
||
double lat_unit = 90.0f / 0x7FFFFF;
|
||
double lon_unit = 180.0f / 0x7FFFFF;
|
||
double lon_sf = 1.0f; //float value we can multiple longitude with
|
||
double lat_sf = 1.0f; //float value we can multiple latitude with
|
||
|
||
char latstr[3];
|
||
char lonstr[3];
|
||
char valid[9];
|
||
sprintf (latstr, "%s", "N");
|
||
sprintf (lonstr, "%s", "E");
|
||
sprintf (valid, "%s", "Current");
|
||
|
||
double latitude = 0;
|
||
double longitude = 0;
|
||
|
||
if (pf) fprintf (stderr, " Protected");
|
||
else
|
||
{
|
||
if (lat_sign)
|
||
{
|
||
sprintf (latstr, "%s", "S");
|
||
lat_sf = -1.0f;
|
||
}
|
||
latitude = ((double)lat * lat_unit);
|
||
|
||
if (lon_sign)
|
||
{
|
||
sprintf (lonstr, "%s", "W");
|
||
lon_sf = -1.0f;
|
||
}
|
||
longitude = ((double)lon * lon_unit);
|
||
|
||
//sanity check
|
||
if (abs (latitude) < 90 && abs(longitude) < 180)
|
||
{
|
||
fprintf (stderr, " Lat: %.5lf%s%s Lon: %.5lf%s%s (%.5lf, %.5lf) ", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, latitude * lat_sf, longitude * lon_sf);
|
||
|
||
if (expired)
|
||
{
|
||
fprintf (stderr, "Expired; ");
|
||
sprintf (valid, "%s", "Expired");
|
||
}
|
||
else if (!expired)
|
||
fprintf (stderr, "Current; ");
|
||
|
||
if (res_a)
|
||
fprintf (stderr, "RES_A: %d; ", res_a);
|
||
|
||
if (res_b)
|
||
fprintf (stderr, "RES_B: %02X; ", res_b);
|
||
|
||
//save to array for ncurses
|
||
sprintf (state->dmr_embedded_gps[slot], "GPS: %lf%s%s %lf%s%s %s", latitude, deg_glyph, latstr, longitude, deg_glyph, lonstr, valid);
|
||
|
||
//save to LRRP report for mapping/logging
|
||
FILE * pFile; //file pointer
|
||
if (opts->lrrp_file_output == 1)
|
||
{
|
||
int src = 0;
|
||
if (slot == 0) src = state->lastsrc;
|
||
if (slot == 1) src = state->lastsrcR;
|
||
|
||
//open file by name that is supplied in the ncurses terminal, or cli
|
||
pFile = fopen (opts->lrrp_out_file, "a");
|
||
fprintf (pFile, "%s\t", getDateL() );
|
||
fprintf (pFile, "%s\t", getTimeL() );
|
||
fprintf (pFile, "%08d\t", src);
|
||
fprintf (pFile, "%.5lf\t", latitude);
|
||
fprintf (pFile, "%.5lf\t", longitude);
|
||
fprintf (pFile, "0\t " ); //zero for velocity
|
||
fprintf (pFile, "0\t " ); //zero for azimuth
|
||
fprintf (pFile, "\n");
|
||
fclose (pFile);
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
fprintf (stderr, "%s", KNRM);
|
||
} |