lightaprs / LightAPRS-W-1.0

Arduino based APRS & WSPR Tracker
GNU General Public License v3.0
62 stars 15 forks source link

Avoid Positions with 60.0 minutes #7

Closed dl9sau closed 1 week ago

dl9sau commented 2 years ago

updatePosition uses dtostrf for ggmin.nn lat/lon encoding. Imagine we're at 53 deg, 59.99 min north. -> Everything is still fine. The used dtostrf() does, like sprintf("%5.2f", ..) function, does upounding. This is ok, because at 53.3096 minutes, we are closer to 53.31 than to 53.30.

It fails, if our position is 59.996nnnnnnn. Then minute is rounded to 60.00, and our position at 53° 59.996min north (with aprs notation 5399.99N) will become 5360.00N. In this case, we do not really need to change degrees to 54 and minutes to 00.00. It's enough to assert 53.99nnnnn will not become 60.00.

Ugly fix:

  dtostrf(dm_lat, 7, 2, latStr);
  // dtostrf rounds up -> > 59.99% will result to 60.0
  if (!strncmp(latStr + 2, "60."), 3)
    sprintf(latStr+2, "59.99");

...

  dtostrf(dm_lon, 8, 2, lonStr);
   if (!strncmp(lonStr + 3, "60."), 3)
    sprintf(lonStr+3, "59.99");

===

dtostrf() has some other limitations. I.e. dtostrf(dm_lat, 7, 2, latStr); for dm_lat 5301.23 (53 degrees) is ok, but for 901.23 (9 degrees), the resulting string will be " 901.23" (leading blank). The unavailable sprintf("%07.4f", ..) is better in this.. ^space You mitigate that problem with

  if (dm_lat < 1000) {
    latStr[0] = '0';
  } 

But what's up with 0 degrees? ;)

If fixed this with

  char *p;

  // smaller numbers lead to leading blanks. Replace by '0', for correct aprs positions
  for (p = lonStr; *p && *p == ' '; p++)
    *p = '0';

Furthermore, I strongly recommend to terminate the string in pos 8 (latStr) and pos 9 (lonStr):

  if (d_lat >= 0.0) {
    latStr[7] = 'N';
  } else {
    latStr[7] = 'S';
  }
  latStr[8] = 0;

===

Better approach:

I stumbled over this extension // APRS PRECISION AND DATUM OPTION http://www.aprs.org/aprs12/datum.txt ; this extension should be added at end of beacon message. .. and needed to prevent unexpected rounding.

I avoided dtostrf and rounded for myself, in a precision I needed:

=>

void updatePosition(int high_precision, char *dao) {
  // Convert and set latitude NMEA string Degree Minute Hundreths of minutes ddmm.hh[S,N].

  char latStr[10];
  RawDegrees rawDeg = gps.location.rawLat();
  uint32_t min_nnnnn;
  char lat_dao = 0;
  min_nnnnn = rawDeg.billionths * 0.006;
  if ( ((min_nnnnn / (high_precision ? 1 : 100)) % 10) >= 5 && min_nnnnn < (6000000 - ((high_precision ? 1 : 100)*5)) ) {
    // round up. Avoid overflow (59.999999 should never become 60.0 or more)
    min_nnnnn = min_nnnnn + (high_precision ? 1 : 100)*5;
  }
  sprintf(latStr, "%02u%02u.%02u%c", (unsigned int ) (rawDeg.deg % 100), (unsigned int ) ((min_nnnnn / 100000) % 100), (unsigned int ) ((min_nnnnn / 1000) % 100), rawDeg.negative ? 'S' : 'N');
  if (dao)
    dao[0] = (char) ((min_nnnnn % 1000) / 11) + 33;

  APRS_setLat(latStr);

  // Convert and set longitude NMEA string Degree Minute Hundreths of minutes ddmm.hh[E,W].

  char lonStr[10];

  min_nnnnn = rawDeg.billionths * 0.006;
  if ( ((min_nnnnn / (high_precision ? 1 : 100)) % 10) >= 5 && min_nnnnn < (6000000 - ((high_precision ? 1 : 100)*5)) ) {
    // round up. Avoid overflow (minute 59.999999 should never become 60.0 or more)
    min_nnnnn = min_nnnnn + (high_precision ? 1 : 100)*5;
  }
  sprintf(lonStr, "%03u%02u.%02u%c", (unsigned int ) (rawDeg.deg % 1000), (unsigned int ) ((min_nnnnn / 100000) % 100), (unsigned int ) ((min_nnnnn / 1000) % 100), rawDeg.negative ? 'W' : 'E');
  if (dao) {
    dao[1] = (char) ((min_nnnnn % 1000) / 11) + 33;
    dao[2] = 0;
  }

  APRS_setLon(lonStr);

}

My updateTelemetry() (where I moved updatePosition() to) ends with

 ..
  sprintf(telemetry_buff + 59 , "%s", comment);
  // APRS PRECISION AND DATUM OPTION http://www.aprs.org/aprs12/datum.txt ; this extension should be added at end of beacon message.
  // We only send this detailed info if it's likely we're interested in, i.e. searching for landing position
  if (send_aprs_enhanced_precision && !ublox_high_alt_mode_enabled && strlen(telemetry_buff) < sizeof(telemetry_buff) - 1 - 5 - 1) /* room for " !wAB!\0" */ {
    char dao[3];
    updatePosition(1, dao);
    sprintf(telemetry_buff + strlen(telemetry_buff), " !w%s!", dao);
  } else {
    updatePosition(0, NULL);
  }

For debuging purposes and prove everything is precise, I added sprintf(telemetry_buff + strlen(telemetry_buff), " debug: %lu %lu", (uint32_t ) gps.location.rawLat().billionths, (uint32_t ) gps.location.rawLng().billionths); and saw those correct reports (s. findu.com):

20211002222705,DL9SAU-15>APLIGA,WIDE1-1,WIDE2-1,qAU,DJ2DS-10:/222648h5331.05N/00733.96E[223/000/A=000085 051TxC 18.00C 996.04hPa 4.69V 03S HF14 Thomas D23 debug: 517633000 566033000 !wj3!
20211002230235,DL9SAU-15>APLIGA,WIDE1-1,WIDE2-1,qAU,DJ2DS-10:/230221h5331.06N/00733.95E[348/005/A=000062 052TxC 18.20C 995.69hPa 4.72V 04S HF14 Thomas D23 debug: 517730333 565894167 !wCB!
lightaprs commented 2 years ago

Thank you Thomas, have you tested your fix with a pico balloon launch? If you think your fix is working for other locations, you can create a pull request.

dl9sau commented 2 years ago

Hello Mustafa,

Am 03.10.2021 um 14:53 schrieb Mustafa Tan @.***>:

Thank you Thomas, have you tested your fix with a pico balloon launch?

I've testet it on the ground and I verified it by entering various gps locations by hand.

I also proved the rounding problem at 59.9999 with dtostrf().

Btw, upon request of dg4nob (who is very active in pico ballooning), I implemented the WSPR S4 encoding extension (https://qrp-labs.com/flights/s4.html) I also implemented WSPR type2 and type3 messages, HF band hopping (according to "local time" of current longitude), etc..

During the last weeks, I spent time for FT8 position/telemetry encoding. I hoped, pskreporter.info will show my sent locations and telemetry - but unfortunately, it doesn't. -> This is still work in progress.

$ git diff LightAPRS-W-pico-balloon.ino | wc -l 2431 ;)

I liked to show you what I've done, but I've not found your E-Mail contact.

An other interesting option is the js8call mode (i.e. their aprs positioning format); js8cll also has many listeners around the world. Unfortunately, there's no embedded controller library available. I asked the developer for some info; I still hope he'll answer. Reverse-engineering would be a hard job (complex code, much fortran and c++ wrappers, lack of documentation).

If you think your fix is working for other locations, you can create a pull request.

ok

vy 73,

lightaprs commented 2 years ago

you contact me via support@lightaprs.com