Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions src/MeadeCommandProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,33 @@ meade::DecCoordinate decFrom(const Declination &d)
// correction before splitting into components.
int deg, min, sec;
d.getCelestialDegrees(deg, min, sec);
// getCelestialDegrees folds the sign into `deg`, where anything between 0
// and -1 degrees comes back as +0. Read the sign off the undivided total.
const long celestialSeconds = Declination::axisToCelestialSeconds(d.getTotalSeconds(), inNorthernHemisphere);
return meade::DecCoordinate {
static_cast<int16_t>(deg),
static_cast<uint16_t>(deg < 0 ? -deg : deg),
static_cast<uint8_t>(min),
static_cast<uint8_t>(sec),
celestialSeconds < 0,
};
}

Declination decFromWire(meade::DecCoordinate const &d)
{
return Declination::fromCelestialDegrees(d.degrees, d.minutes, d.seconds);
// fromCelestialDegrees carries the sign in its `deg` parameter, so a
// coordinate such as "-00*30:00" still arrives there unsigned. The parser
// keeps sign and magnitude apart up to this call.
const int degrees = d.negative ? -static_cast<int>(d.degrees) : static_cast<int>(d.degrees);
return Declination::fromCelestialDegrees(degrees, d.minutes, d.seconds);
}

// Signed arc-seconds for a magnitude/sign pair. The Latitude and Longitude
// constructors take signed degrees, which cannot express a site between 0 and
// -1 degree, so callers add this total to a zeroed coordinate instead.
long siteSecondsFrom(uint16_t degrees, uint8_t minutes, bool negative)
{
const long seconds = ((static_cast<long>(degrees) * 60L) + minutes) * 60L;
return negative ? -seconds : seconds;
}
} // namespace

Expand Down Expand Up @@ -161,18 +178,24 @@ bool MeadeCommandProcessor::onIsGuiding()
meade::MeadeLatitude MeadeCommandProcessor::onSiteLatitude()
{
const Latitude lat = _mount->latitude();
// getHours() folds the sign into the degrees component, so a site between
// 0 and -1 degrees reports as +0. Read the sign off the total instead.
const int degrees = lat.getHours();
return meade::MeadeLatitude {
static_cast<int16_t>(lat.getHours()),
static_cast<uint16_t>(degrees < 0 ? -degrees : degrees),
static_cast<uint8_t>(lat.getMinutes()),
lat.getTotalSeconds() < 0,
};
}

meade::MeadeLongitude MeadeCommandProcessor::onSiteLongitude()
{
const Longitude lon = _mount->longitude();
const int degrees = lon.getHours();
return meade::MeadeLongitude {
static_cast<int16_t>(lon.getHours()),
static_cast<uint16_t>(degrees < 0 ? -degrees : degrees),
static_cast<uint8_t>(lon.getMinutes()),
lon.getTotalSeconds() < 0,
};
}

Expand Down Expand Up @@ -300,13 +323,17 @@ bool MeadeCommandProcessor::onSyncCoordinates(meade::DecCoordinate dec, meade::R

bool MeadeCommandProcessor::onSetSiteLatitude(meade::MeadeLatitude lat)
{
_mount->setLatitude(Latitude(static_cast<int>(lat.degrees), static_cast<int>(lat.minutes), 0));
Latitude value;
value.addSeconds(siteSecondsFrom(lat.degrees, lat.minutes, lat.negative));
_mount->setLatitude(value);
return true;
}

bool MeadeCommandProcessor::onSetSiteLongitude(meade::MeadeLongitude lon)
{
_mount->setLongitude(Longitude(static_cast<int>(lon.degrees), static_cast<int>(lon.minutes), 0));
Longitude value;
value.addSeconds(siteSecondsFrom(lon.degrees, lon.minutes, lon.negative));
_mount->setLongitude(value);
return true;
}

Expand Down
22 changes: 16 additions & 6 deletions src/core/meade/MeadeParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,23 +160,33 @@ struct RaCoordinate {
uint8_t seconds;
};

/** @brief Declination coordinate; `degrees` carries the sign (-180..180). */
/**
* @brief Declination coordinate: unsigned magnitude plus a separate sign.
*
* The sign is a field of its own rather than the sign bit of `degrees`
* because the Meade wire format has coordinates such as `-00*30:00` whose
* degrees component is zero; folding the sign into `degrees` would round
* those to `+00*30:00`, a one-degree error either side of the equator.
*/
struct DecCoordinate {
int16_t degrees;
uint16_t degrees; ///< Magnitude only, 0..180.
uint8_t minutes;
uint8_t seconds;
bool negative;
};

/** @brief Site latitude; `degrees` is signed (-90..90). */
/** @brief Site latitude: magnitude 0..90 in `degrees`, sign in `negative`. */
struct MeadeLatitude {
int16_t degrees;
uint16_t degrees;
uint8_t minutes;
bool negative;
};

/** @brief Site longitude; `degrees` is signed (-180..180). */
/** @brief Site longitude: magnitude 0..180 in `degrees`, sign in `negative`. */
struct MeadeLongitude {
int16_t degrees;
uint16_t degrees;
uint8_t minutes;
bool negative;
};

/** @brief Wall-clock time (24h). The parser handles 12h conversion for `:Ga#`. */
Expand Down
64 changes: 21 additions & 43 deletions src/core/meade/MeadeParserHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ bool Cursor::matchIn(const char *set)
return false;
}

void Cursor::advance()
{
if (*_p != '\0')
{
++_p;
}
}

bool Cursor::digits(int n, unsigned &out)
{
unsigned v = 0;
Expand All @@ -79,29 +87,14 @@ bool Cursor::digits(int n, unsigned &out)
return true;
}

bool Cursor::signed2(int &out)
{
char sign = peek();
if (sign != '+' && sign != '-')
return false;
++_p;
unsigned v = 0;
if (!digits(2, v))
return false;
out = (sign == '-') ? -static_cast<int>(v) : static_cast<int>(v);
return true;
}

bool Cursor::signed3(int &out)
bool Cursor::optionalSign(int &sign)
{
char sign = peek();
if (sign != '+' && sign != '-')
return false;
++_p;
unsigned v = 0;
if (!digits(3, v))
return false;
out = (sign == '-') ? -static_cast<int>(v) : static_cast<int>(v);
const char c = peek();
sign = (c == '-') ? -1 : 1;
if ((c == '+') || (c == '-'))
{
advance();
}
return true;
}

Expand Down Expand Up @@ -230,13 +223,8 @@ void writeRa(MeadeResponse &r, const RaCoordinate &ra)

void writeDec(MeadeResponse &r, const DecCoordinate &d)
{
int deg = d.degrees;
writeChar(r, deg < 0 ? '-' : '+');
if (deg < 0)
{
deg = -deg;
}
writeUnsignedPadded(r, static_cast<unsigned>(deg), 2);
writeChar(r, d.negative ? '-' : '+');
writeUnsignedPadded(r, d.degrees, 2);
writeChar(r, '*');
writeUnsignedPadded(r, d.minutes, 2);
writeChar(r, '\'');
Expand All @@ -246,27 +234,17 @@ void writeDec(MeadeResponse &r, const DecCoordinate &d)

void writeLatitude(MeadeResponse &r, const MeadeLatitude &l)
{
int deg = l.degrees;
writeChar(r, deg < 0 ? '-' : '+');
if (deg < 0)
{
deg = -deg;
}
writeUnsignedPadded(r, static_cast<unsigned>(deg), 2);
writeChar(r, l.negative ? '-' : '+');
writeUnsignedPadded(r, l.degrees, 2);
writeChar(r, '*');
writeUnsignedPadded(r, l.minutes, 2);
writeTerminator(r);
}

void writeLongitude(MeadeResponse &r, const MeadeLongitude &l)
{
int deg = l.degrees;
writeChar(r, deg < 0 ? '-' : '+');
if (deg < 0)
{
deg = -deg;
}
writeUnsignedPadded(r, static_cast<unsigned>(deg), 3);
writeChar(r, l.negative ? '-' : '+');
writeUnsignedPadded(r, l.degrees, 3);
writeChar(r, '*');
writeUnsignedPadded(r, l.minutes, 2);
writeTerminator(r);
Expand Down
21 changes: 13 additions & 8 deletions src/core/meade/MeadeParserHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ namespace meade
// ---------------------------------------------------------------------------
// Cursor — single-pass input cursor with small grammar primitives
//
// Forward-only; never backtracks. Each primitive returns `false` on mismatch
// (cursor is advanced on success). Ideal for fixed-format Meade sub-commands
// like coordinates, times, and dates.
// Forward-only; never backtracks. The matching primitives return `false` on
// mismatch and advance only on success. The two unconditional ones are the
// exception: `advance` returns nothing and `optionalSign` always returns
// `true`. Ideal for fixed-format Meade sub-commands like coordinates, times,
// and dates.
// ---------------------------------------------------------------------------

class Cursor
Expand All @@ -43,14 +45,17 @@ class Cursor
/// Consume one character if it is any of the chars in `set`.
bool matchIn(const char *set);

/// Consume one character unconditionally; a no-op at end of input.
void advance();

/// Read exactly `n` decimal digits into `out` (big-endian, no separators).
bool digits(int n, unsigned &out);

/// Read "+DD" or "-DD" into a signed int.
bool signed2(int &out);

/// Read "+DDD" or "-DDD" into a signed int.
bool signed3(int &out);
/// Consume a leading '+' or '-' if present and report it in `sign` as -1
/// or +1 (+1 when absent). Always succeeds — callers that require a sign
/// check `peek()` first. Keeping the sign out of the magnitude is what
/// lets "-00" survive; a signed magnitude cannot hold it.
bool optionalSign(int &sign);

private:
const char *_p;
Expand Down
57 changes: 38 additions & 19 deletions src/core/meade/MeadeParserSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,34 @@ namespace meade
namespace
{

// The readers below have always required an explicit sign, and this keeps
// that grammar byte-for-byte unchanged. It is a description of the parser as
// it stands, not of the protocol: MeadeProtocol.hpp documents the sign as
// optional for `:Sg`, where an unsigned value means 0..360 going westward.
// That form is rejected here, exactly as it was before this change.
bool readMandatorySign(Cursor &c, int &sign)
{
const char first = c.peek();
if ((first != '+') && (first != '-'))
{
return false;
}
return c.optionalSign(sign);
}

// Format: "[+-]DD<sep>MM:SS" where sep in {'*', ':'}.
bool readDecCoordinate(Cursor &c, DecCoordinate &out)
{
int deg;
unsigned mm, ss;
if (!c.signed2(deg) || !c.matchIn("*:") || !c.digits(2, mm) || !c.match(':') || !c.digits(2, ss))
int sign;
unsigned dd, mm, ss;
if (!readMandatorySign(c, sign) || !c.digits(2, dd) || !c.matchIn("*:") || !c.digits(2, mm) || !c.match(':') || !c.digits(2, ss))
{
return false;
}
out.degrees = static_cast<int16_t>(deg);
out.minutes = static_cast<uint8_t>(mm);
out.seconds = static_cast<uint8_t>(ss);
out.degrees = static_cast<uint16_t>(dd);
out.minutes = static_cast<uint8_t>(mm);
out.seconds = static_cast<uint8_t>(ss);
out.negative = (sign < 0);
return true;
}

Expand All @@ -51,28 +67,30 @@ bool readRaCoordinate(Cursor &c, RaCoordinate &out)
// Format: "[+-]DD<sep>MM" where sep in {'*', ':'}.
bool readLatitude(Cursor &c, MeadeLatitude &out)
{
int deg;
unsigned mm;
if (!c.signed2(deg) || !c.matchIn("*:") || !c.digits(2, mm))
int sign;
unsigned dd, mm;
if (!readMandatorySign(c, sign) || !c.digits(2, dd) || !c.matchIn("*:") || !c.digits(2, mm))
{
return false;
}
out.degrees = static_cast<int16_t>(deg);
out.minutes = static_cast<uint8_t>(mm);
out.degrees = static_cast<uint16_t>(dd);
out.minutes = static_cast<uint8_t>(mm);
out.negative = (sign < 0);
return true;
}

// Format: "[+-]DDD<sep>MM" where sep in {'*', ':'}.
bool readLongitude(Cursor &c, MeadeLongitude &out)
{
int deg;
unsigned mm;
if (!c.signed3(deg) || !c.matchIn("*:") || !c.digits(2, mm))
int sign;
unsigned ddd, mm;
if (!readMandatorySign(c, sign) || !c.digits(3, ddd) || !c.matchIn("*:") || !c.digits(2, mm))
{
return false;
}
out.degrees = static_cast<int16_t>(deg);
out.minutes = static_cast<uint8_t>(mm);
out.degrees = static_cast<uint16_t>(ddd);
out.minutes = static_cast<uint8_t>(mm);
out.negative = (sign < 0);
return true;
}

Expand Down Expand Up @@ -223,13 +241,14 @@ void handleMeadeSet(MeadeResponse &r, const char *s, IMeadeSetHandlers &h)
case 'G':
{
// G<sign><DD>
int hours;
if (!c.signed2(hours))
int sign;
unsigned hours;
if (!readMandatorySign(c, sign) || !c.digits(2, hours))
{
writeChar(r, '0');
return;
}
writeSetAck(r, h.onSetUtcOffset(hours));
writeSetAck(r, h.onSetUtcOffset(sign * static_cast<int>(hours)));
return;
}

Expand Down
Loading