Unable to get past an `App fault!` when running trig calculations in the cloudpebble emulator

I’m trying to design a watchface using cloudpebble, and I’m struggling to get going.
My idea requires doing some calculations for finding the current sun position in the sky, but I can’t seem to use trigonometry functions consistently without crashing with an App fault! message, like this one:
[ERROR] ault_handling.c:99: App fault! {8a392ece-5518-4876-9eb6-c5d2e01f3c79} PC: 0x30f2 LR: 0x1

I also haven’t been able to isolate the problem to a specific trig function, or a specific input value that causes issues, as it seems very sensitive to contextual changes (as in, just calling the exact same function from a different place, or a different number of times, can cause the issue to appear or disappear).

My main.hfile consists of the first beginner demo, with my suncalc.h included, and with a call to my problemsolar_lunar_data function inside the update_time function.

This is a subsection of the problem code:

#include <math.h>
#include <pebble.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif


typedef struct
{
    double solar_midnight;
    double sunrise;
    double solar_noon;
    double sunset;
    double lunar_max_height;
    double lunar_min_height;
    int moon_phase;
} SolarLunarData;


SolarLunarData compute_solar_lunar_data(double latitude_degrees,
                                        double longitude_degrees,
                                        unsigned int current_unix_timestamp)
{
    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 1");
    // ============================================================
    // SECTION 1 — CONSTANTS AND TIME CONVERSION
    // ============================================================
    const double seconds_per_day = 86400.0;
    const double unix_to_julian_offset = 2440587.5;

    double julian_date = ((double)current_unix_timestamp / seconds_per_day) + unix_to_julian_offset;
    double days_since_j2000 = julian_date - 2451545.0;

    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 2");
    // ============================================================
    // SECTION 2 — SOLAR POSITION CALCULATIONS
    // ============================================================
    double mean_solar_longitude = fmod(280.460 + 0.9856474 * days_since_j2000, 360.0);
    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 2.1");

    double mean_anomaly_sun = fmod(357.528 + 0.9856003 * days_since_j2000, 360.0);
    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 2.2");

    double mean_anomaly_radians = mean_anomaly_sun * M_PI / 180.0;
    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 2.3");

    double ecliptic_longitude = mean_solar_longitude
        + 1.915 * sin(mean_anomaly_radians)
        + 0.020 * sin(2.0 * mean_anomaly_radians);
    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 2.4");
  
    double ecliptic_longitude_radians = ecliptic_longitude * M_PI / 180.0;
    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 2.5");
  
    double obliquity_radians = (23.439 - 0.0000004 * days_since_j2000) * M_PI / 180.0;
    APP_LOG(APP_LOG_LEVEL_DEBUG, "section 2.6");

(If you’re curious, this was originally a much more tidy and complete suncalc implementation, but I shoved all the stuff I needed into a single function to try pinpointing the issue I’m having).

As you can see, I stuck a bunch of APP_LOGs in there to try and identify the point where the function stops running. This is what gets printed to the log:

[DEBUG] suncalc.h:32: section 1
[DEBUG] suncalc.h:42: section 2
[DEBUG] suncalc.h:47: section 2.1
[DEBUG] suncalc.h:50: section 2.2
[DEBUG] suncalc.h:53: section 2.3
[ERROR] ault_handling.c:99: App fault! {8a392ece-5518-4876-9eb6-c5d2e01f3c79} PC: 0x30f2 LR: 0x1

During a previous run of the same code, it only got to section 2.2 before crashing, but it seems like it usually gets to section 2.3 now…

Anyways, I haven’t tried setting up a (non-cloudpebble) development environment for Pebble on my PC yet, but that’s probably going to be my next step. I have to stop debugging for now, just to preserve my sanity, but I wanted to post this first, in case anyone here happens to have any insight they can offer :sweat_smile:

2 Likes

The Pebble C API doesn’t support the usual trigonometry functions sin(), cos() etc. but instead has sin_lookup(), cos_lookup() and atan2_lookup() functions with pre-computed tables:

And a question: do you intend to publish your suncalc implementation as open source? I would be very interested in this :slight_smile:

2 Likes

Oh my god, thank you! I was able to get the function to return by substituting some approximation functions in place of the real trig functions.

I might open source it. The logic in my current code is based on my MicroPython suncalc port suncalc.mpy, which itself is based on a Python/numpy port of suncalc.js. I was planning on converting it into a suncalc.h, which I would then publish separately from my Pebble app, for anyone who wanted something like that… But right now, my fixed code completely sucks and doesn’t have most of the features of the source suncalc modules :sweat_smile:
I might still open-source my junky code anyways. Or, maybe I’ll make a nice Pebble-specific suncalc.h module I can share; I’ll have to think about it! (and see how much time/energy I’ve got!)

2 Likes

Hey, I think I know your problem pretty well – this is a classic stack overflow on the Pebble.

The actual issue isn’t a specific trigonometric function or a specific input value, but rather the stack memory, which is extremely limited on the Pebble (only a few KB per app). double variables each consume 8 bytes, and once you stack enough of them up – plus the overhead of the trig functions from math.h – you run out of stack space. The fact that it crashes sometimes at 2.2 and sometimes at 2.3 is a typical symptom of exactly this: the stack is at a different fill level depending on the context your function is called from.

An approach that personally helped me a lot:

I moved such calculations (sunrise, sunset for example) completely out of the C code and implemented them in index.js (the phone-side JavaScript) instead. The phone has real floating-point hardware and full access to Math.sin(), Math.cos() etc. – nothing crashes there.

I then send the results once (or e.g. hourly) to the watchface via AppMessage / Message Keys. In the C code I only receive the pre-calculated values – for example sunrise and sunset as a Unix timestamp or as simple integers (minutes since midnight), which I can display directly.

This has several advantages:

  • No more stack issues on the watch

  • The watch battery is spared, since no expensive calculations are running on it

  • Perfect if you really don’t need such values recalculated every minute

For the watch-side display (e.g. a hand showing the current sun position) simple integer arithmetic with the received values is then enough – no double and no math.h needed at all.

Good luck – Pebble development is genuinely fun despite these quirks! :blush:

2 Likes

Thanks so much for the deeper explanation on the error! As I briefly mentioned on the post above, the math.h trig functions did occasionally work for me, so your explanation does make more sense than the functions just not being supported, haha.

And yes, as I finished the development of this watch face (Celestial Noon, I decided to call it!) I came to the same realization that it probably made more sense to just do that calculation on the phone.
Originally I wanted it all to be done on the watch because I didn’t want to have to also write a JavaScript component on the phone… But then I realized that I’d still have to write that JavaScript component in order to get location information, so it didn’t end up mattering much in the end (and it ended up being fairly easy, anyways), and I could have just used the original suncalc.js module if I had done that, haha.

Anyways, for now, I did get the watch face working, with the calculation done locally on the watch! I also made it so the location was stored in persistent storage on the watch face, so it should continue to work when away from your phone (after that initial set up).
I also limited it so that the sun/moon calculations are only done every half hour (and thinking about it again now, I could probably reduce that further, since the timestamps shouldn’t actually change that often…) so hopefully it doesn’t drain the battery too much :man_tipping_hand: but if I do suncalc stuff again in the future, I will probably just do it all on the phone side, next time.

1 Like

Congratulations on publishing the watchface - it looks great (already hearted it)!

2 Likes