TEST test_entry_write_multiple(void)
{
    uint32_t pos_a = test_logger.flash_write_pos;
    cmp_ctx_t *ctx;
    ctx = _log_entry_create(&test_logger, "a");
    cmp_write_integer(ctx, 42);
    _log_entry_write_to_flash(&test_logger);

    uint32_t pos_b = test_logger.flash_write_pos;
    ASSERT(pos_a < pos_b);
    ctx = _log_entry_create(&test_logger, "b");
    cmp_write_integer(ctx, 23);
    _log_entry_write_to_flash(&test_logger);

    cmp_ctx_t reader;
    cmp_mem_access_t cma;
    cmp_mem_access_ro_init(&reader, &cma, &flash_array[pos_a + LOG_ENTRY_HEADER_LEN], LOG_ENTRY_DATA_LEN);
    uint32_t size = 0;
    uint64_t timestamp = 0;
    int64_t val = 0;
    char name[10];
    bool ok;
    size = sizeof(name);
    ok = true;
    ok &= cmp_read_array(&reader, &size);
    ok &= cmp_read_uinteger(&reader, &timestamp);
    ok &= cmp_read_str(&reader, name, &size);
    ok &= cmp_read_integer(&reader, &val);
    ASSERT(ok);
    ASSERT_EQ(val, 42);

    size_t pos = cmp_mem_access_get_pos(&cma);
    ASSERT_EQ(pos, flash_array[0]);
    ASSERT_EQ(pos + LOG_ENTRY_HEADER_LEN, pos_b);

    cmp_mem_access_ro_init(&reader, &cma, &flash_array[pos_b + LOG_ENTRY_HEADER_LEN], LOG_ENTRY_DATA_LEN);
    size = sizeof(name);
    ok = true;
    ok &= cmp_read_array(&reader, &size);
    ok &= cmp_read_uinteger(&reader, &timestamp);
    ok &= cmp_read_str(&reader, name, &size);
    ok &= cmp_read_integer(&reader, &val);
    ASSERT(ok);
    ASSERT_EQ(val, 23);
    ASSERT_EQ(flash_array[pos_b], cmp_mem_access_get_pos(&cma));

    PASS();
}
TEST test_entry_header(void)
{
    _log_entry_create(&test_logger, "entry");

    cmp_ctx_t reader;
    cmp_mem_access_t cma;
    cmp_mem_access_ro_init(&reader, &cma, &test_logger.buffer[LOG_ENTRY_HEADER_LEN], LOG_ENTRY_DATA_LEN);
    uint32_t size = 0;
    if (!cmp_read_array(&reader, &size) || size != 3) {
        FAIL();
    }
    uint64_t timestamp = 0;
    if (!cmp_read_uinteger(&reader, &timestamp) || timestamp != logger_timestamp_sec()) {
        FAIL();
    }
    char name[10];
    size = sizeof(name);
    if (!cmp_read_str(&reader, name, &size) || strcmp(name, "entry") != 0) {
        FAIL();
    }
    PASS();
}
void can_bridge_datagram_rcv_cb(const void *data, size_t len, void *arg)
{
    uint32_t cmd;
    cmp_mem_access_t cma;
    cmp_ctx_t ctx;
    (void) arg;

    cmp_mem_access_ro_init(&ctx, &cma, data, len);

    // read command
    if (!cmp_read_uint(&ctx, &cmd)) {
        return;
    }

    switch (cmd) {
    case CMD_CAN_FRAME:
        can_send_cmp_frame(&ctx);
        break;
    case CMD_CFG_ID_FILTER:
        can_bridge_set_filter(&ctx);
        break;
    }
}
TEST test_entry_write_and_readback(void)
{
    cmp_ctx_t *ctx = _log_entry_create(&test_logger, "entry");
    cmp_write_integer(ctx, 42);
    _log_entry_write_to_flash(&test_logger);

    uint8_t buf[LOG_ENTRY_DATA_LEN];
    uint32_t next_entry = 0;
    size_t len = 0;
    if (!log_read_entry(0, buf, &len, &next_entry)) {
        FAILm("CRC missmatch");
    }
    ASSERT_EQ(test_logger.flash_write_pos, next_entry);

    // readback entry
    cmp_ctx_t reader;
    cmp_mem_access_t cma;
    cmp_mem_access_ro_init(&reader, &cma, buf, sizeof(buf));
    uint32_t size = 0;
    if (!cmp_read_array(&reader, &size) || size != 3) {
        FAIL();
    }
    uint64_t timestamp = 0;
    if (!cmp_read_uinteger(&reader, &timestamp) || timestamp != logger_timestamp_sec()) {
        FAIL();
    }
    char name[10];
    size = sizeof(name);
    if (!cmp_read_str(&reader, name, &size) || strcmp(name, "entry") != 0) {
        FAIL();
    }
    int64_t data = 0;
    if (!cmp_read_integer(&reader, &data) || data != 42) {
        FAIL();
    }
    PASS();
}
int main(int argc, char const *argv[])
{
    zmq::context_t context(1);

    zmq::socket_t subscriber (context, ZMQ_SUB);
    subscriber.connect("tcp://localhost:5556");

    subscriber.setsockopt(ZMQ_SUBSCRIBE, NULL, 0);


    EKFGyroAccMag kalman;

    bool mag_calibration_ready = false;
    sleep(3);
    float prev_timestamp = 0;
    // int i = 1000;
    // int skip = 10;
    while (1) {
        zmq::message_t update;

        subscriber.recv(&update);


        // if (i > 0) {
        //     i--;
        //     continue;
        // }

        cmp_ctx_t cmp;
        cmp_mem_access_t mem;
        cmp_mem_access_ro_init(&cmp, &mem, update.data(), update.size());

        char strbuf[100];
        bool err = false;
        uint32_t strsize = sizeof(strbuf);
        err = err || !cmp_read_str(&cmp, &strbuf[0], &strsize);
        if (err) {
            continue;
        }
        // std::cerr << "received: " << strbuf << std::endl;
        if (strcmp("imu", strbuf) == 0) {
            uint32_t mapsize = 0;
            err = err | !cmp_read_map(&cmp, &mapsize);
            bool gyro_ok = false;
            bool acc_ok = false;
            bool time_ok = false;
            float gyro[3];
            float acc[3];
            float timestamp;
            for (int i = 0; i < mapsize; i++) {
                char strbuf[100];
                uint32_t strsize = sizeof(strbuf);
                err = err || !cmp_read_str(&cmp, strbuf, &strsize);
                uint32_t arr_sz;
                if (!err && strcmp("gyro", strbuf) == 0) {
                    err = err || !cmp_read_array(&cmp, &arr_sz);
                    err = err || (arr_sz != 3);
                    err = err || !cmp_read_float(&cmp, &gyro[0]);
                    err = err || !cmp_read_float(&cmp, &gyro[1]);
                    err = err || !cmp_read_float(&cmp, &gyro[2]);
                    gyro_ok = !err;
                } else if (!err && strcmp("acc", strbuf) == 0) {
                    err = err || !cmp_read_array(&cmp, &arr_sz);
                    err = err || (arr_sz != 3);
                    err = err || !cmp_read_float(&cmp, &acc[0]);
                    err = err || !cmp_read_float(&cmp, &acc[1]);
                    err = err || !cmp_read_float(&cmp, &acc[2]);
                    acc_ok = !err;
                } else if (!err && strcmp("time", strbuf) == 0) {
                    uint64_t int_timestamp;
                    err = err || !cmp_read_uinteger(&cmp, &int_timestamp);
                    if (!err) {
                        timestamp = ((float) int_timestamp) / 1000000;
                    }
                    time_ok = !err;
                }
            }

            // skip--;
            // if (skip > 0) {
            //     continue;
            // } else {
            //     skip = 10;
            // }

            if (!err && gyro_ok && acc_ok && time_ok) {
                acc[0] = acc[0]/1;
                acc[1] = acc[1]/1;
                acc[2] = acc[2]/1;

                if (prev_timestamp == 0 || !mag_calibration_ready) {
                    prev_timestamp = timestamp;
                    continue;
                }
                float delta_t = timestamp - prev_timestamp;
                prev_timestamp = timestamp;

                kalman.update_imu(gyro, acc, delta_t);

                std::cout << kalman.get_attitude().w() << ", "
                        << kalman.get_attitude().x() << ", "
                        << kalman.get_attitude().y() << ", "
                        << kalman.get_attitude().z() << std::endl;
                // std::cerr << "q: " << kalman.get_attitude().w() << ", "
                //         << kalman.get_attitude().x() << ", "
                //         << kalman.get_attitude().y() << ", "
                //         << kalman.get_attitude().z() << std::endl;

                // std::cerr << kalman.P << std::endl;
                // std::cerr << "time: " << timestamp << std::endl;

                // std::cerr << kalman.P(0, 0) << ", "
                //         << kalman.P(1, 1) << ", "
                //         << kalman.P(2, 2) << ", "
                //         << kalman.P(3, 3) << std::endl;

                // std::cerr << "imu: gyro: " << gyro[0] << ", " << gyro[1] << ", " << gyro[2];
                // std::cerr << "                                                 acc " << acc[0] << ", " << acc[1] << ", " << acc[2] << std::endl;
                // std::cerr << " time" << timestamp << std::endl;
            } else {
                std::cerr << "imu: msgpack read error : " << cmp_strerror(&cmp) << std::endl;
            }

        }
        if (strcmp("mag", strbuf) == 0) {
            static float mag_max[3] = {-1000, -1000, -1000};
            static float mag_min[3] = {1000, 1000, 1000};

            uint32_t mapsize = 0;
            err = err | !cmp_read_map(&cmp, &mapsize);
            bool field_ok = false;
            bool time_ok = false;
            float mag[3];
            uint64_t timestamp;
            for (int i = 0; i < mapsize; i++) {
                char strbuf[100];
                uint32_t strsize = sizeof(strbuf);
                err = err || !cmp_read_str(&cmp, strbuf, &strsize);
                uint32_t arr_sz;
                if (!err && strcmp("field", strbuf) == 0) {
                    err = err || !cmp_read_array(&cmp, &arr_sz);
                    err = err || (arr_sz != 3);
                    err = err || !cmp_read_float(&cmp, &mag[0]);
                    err = err || !cmp_read_float(&cmp, &mag[1]);
                    err = err || !cmp_read_float(&cmp, &mag[2]);
                    field_ok = !err;
                } else if (!err && strcmp("time", strbuf) == 0) {
                    err = err || !cmp_read_uinteger(&cmp, &timestamp);
                    time_ok = !err;
                }
            }

            if (!err && field_ok && time_ok) {
                // std::cerr << "mag field: " << mag[0] << ", " << mag[1] << ", " << mag[2] << std::endl;

                for (int i = 0; i < 3; i++) {
                    if (mag[i] > mag_max[i]) {
                        mag_max[i] = mag[i];
                    }
                    if (mag[i] < mag_min[i]) {
                        mag_min[i] = mag[i];
                    }
                }


                const float EARTH_MAG_FIELD_TH = 0.8*0.4;
                if (mag_max[0] - mag_min[0] > 2*EARTH_MAG_FIELD_TH
                    && mag_max[1] - mag_min[1] > EARTH_MAG_FIELD_TH
                    && mag_max[2] - mag_min[2] > EARTH_MAG_FIELD_TH) {

                    mag_calibration_ready = true;
                    std::cerr << "mag calibration ready" << std::endl;
                }

                float mag_zero[3];
                float mag_calib[3];
                for (int i = 0; i < 3; i++) {
                    mag_zero[i] = (mag_min[i] + mag_max[i]) / 2;
                    mag_calib[i] = mag[i] - mag_zero[i];
                }
                if (mag_calibration_ready) {

                    std::cerr << "calibrated mag field: " << mag_calib[0] << ", " << mag_calib[1] << ", " << mag_calib[2] << std::endl;
                    kalman.update_mag(&mag_calib[0]);

                    float mag_inertial[3];
                    ekf_mag::z_inertial(kalman.x[0], kalman.x[1], kalman.x[2], kalman.x[3], mag_calib[0], mag_calib[1], mag_calib[2], &mag_inertial[0]);
                    std::cerr << "meas " << mag_inertial[0] << ", " << mag_inertial[1] << ", " << mag_inertial[2] << std::endl;
                }
            }
        }
    }

    return 0;
}