Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tNMEA0183Msg::AddDoubleField requires Arduino dtostrf function #63

Open
clickworkorange opened this issue Nov 6, 2024 · 0 comments
Open

Comments

@clickworkorange
Copy link

clickworkorange commented Nov 6, 2024

I've successfully compiled your lovely NMEA0183 library with the arm-none-eabi toolchain for running on an ARM1176. While doing so I ran into an issue with the AddDoubleField method of tNMEA0183Msg; it depends on dtostrf which I'm pretty sure is an Arduino specific function. I solved this by creating an extern C library (noniso.c) which declares a substitute dtostrf as follows (shamelessly stolen from the ESP32 Arduino Core):

char *dtostrf(double number, signed int width, unsigned int prec, char *s) {
  bool negative = false;

  if (isnan(number)) {
    strcpy(s, "nan");
    return s;
  }
  if (isinf(number)) {
    strcpy(s, "inf");
    return s;
  }

  char *out = s;

  int fillme = width;  // how many cells to fill for the integer part
  if (prec > 0) {
    fillme -= (prec + 1);
  }

  // Handle negative numbers
  if (number < 0.0) {
    negative = true;
    fillme--;
    number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  // I optimized out most of the divisions
  double rounding = 2.0;
  for (unsigned int i = 0; i < prec; ++i) {
    rounding *= 10.0;
  }
  rounding = 1.0 / rounding;

  number += rounding;

  // Figure out how big our number really is
  double tenpow = 1.0;
  unsigned int digitcount = 1;
  while (number >= 10.0 * tenpow) {
    tenpow *= 10.0;
    digitcount++;
  }

  number /= tenpow;
  fillme -= digitcount;

  // Pad unused cells with spaces
  while (fillme-- > 0) {
    *out++ = ' ';
  }

  // Handle negative sign
  if (negative) {
    *out++ = '-';
  }

  // Print the digits, and if necessary, the decimal point
  digitcount += prec;
  int8_t digit = 0;
  while (digitcount-- > 0) {
    digit = (int8_t)number;
    if (digit > 9) {
      digit = 9;  // insurance
    }
    *out++ = (char)('0' | digit);
    if ((digitcount == prec) && (prec > 0)) {
      *out++ = '.';
    }
    number -= digit;
    number *= 10.0;
  }

  // make sure the string is terminated
  *out = 0;
  return s;
}

I haven't done any thorough testing (yet), but so far it seems to do what's expected. It might be worth providing something similar as a default substitute in the NMEA0183 library for non-Arduino builds. Alternatively it looks like dtostrf could be dropped in favour of a sprintf call?

Furthermore, I also needed the millis() and delay() functions - as is clearly explained in the NMEA0183Msg.cpp source (thank you!). The assumption is that these would be C library functions, but I'm using a C++ framework which already provides native ARM timer functions, so I changed the externs to C++ and created a small C++ wrapper (avr.cpp) for these two methods:

#include <circle/timer.h>
#include "avr.h"

unsigned millis(void) {
  return CTimer::Get ()->GetClockTicks () / (CLOCKHZ / 1000);
}

void delay(unsigned ms) {
  CTimer::SimpleMsDelay(ms);
}

In NMEA0183Msg.cpp:

...
#ifndef ARDUINO
#include <cstdio>
#include <cstdlib>
#include "noniso.h"
#include "avr.h"
#endif
...

This too seems to work, though it's entirely possible that I've made some mistake or that I've misunderstood something fundamental; C/C++ are not my first languages, or even my second :) Let me know what you think - and many thanks for all your hard work on this great library!

Edit: Having read up on extern I realised that's not how extern works, and that those stanzas were not needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant