Compare commits

...

40 Commits

Author SHA1 Message Date
Roberto Viola
0b6ab0e1cf added gpx file to bundle 2022-06-23 15:48:47 +02:00
Roberto Viola
408b21d0c3 bike 3d now match the actual value from the GPX file 2022-06-23 14:59:01 +02:00
Roberto Viola
1f4ed6f2d2 chart ticks disabled 2022-06-23 09:14:05 +02:00
Roberto Viola
e5e0b84615 fixed position of the chart 2022-06-23 08:48:18 +02:00
Roberto Viola
08f548035d chart Y max size fixed and chart below the metrics always 2022-06-23 08:43:19 +02:00
Roberto Viola
35d6a7b151 fixies 2022-06-22 21:54:31 +02:00
Roberto Viola
de6070464d comment removed 2022-06-22 20:38:19 +02:00
Roberto Viola
e4eb6d0fbe enabling fog and lighting 2022-06-22 16:17:37 +02:00
Roberto Viola
d5a3c6c66e added selection between 2d and 3d maps for gpx 2022-06-22 16:03:33 +02:00
Roberto Viola
3f8d9e8f5b added key to ignore file 2022-06-22 15:01:06 +02:00
Roberto Viola
81b68b3f89 adding elevation chart 2022-06-22 14:58:47 +02:00
Roberto Viola
80b7c6b556 event inclinationNext300Meters added in order to draw a chart in the 3d map 2022-06-22 10:51:40 +02:00
Roberto Viola
254e045cc2 bike azimuth fixed 2022-06-22 09:13:21 +02:00
Roberto Viola
d47098fd1a removed useless UI components and fixed camera collision again 2022-06-22 09:06:59 +02:00
Roberto Viola
215e2be0d6 Merge branch 'Cesium' of https://github.com/cagnulein/qdomyos-zwift into Cesium 2022-06-22 08:39:55 +02:00
Roberto Viola
90de6902a3 fixing altitude in the 3d map 2022-06-22 08:39:51 +02:00
Roberto Viola
107cdd00bd removed useless debug 2022-06-21 22:45:29 +02:00
Roberto Viola
110fadb1a2 Merge branch 'master' into Cesium 2022-06-21 16:31:35 +01:00
Roberto Viola
b915cf0d59 fixed camera collision to the ground; added bike model; added altitude to the metrics 2022-06-21 17:30:45 +02:00
Roberto Viola
4921c37b0a improving smoothness of the map 2022-06-19 09:57:20 +02:00
Roberto Viola
aa863edd11 smoothing altitude and azimuth differences 2022-06-18 06:00:18 +02:00
Roberto Viola
7fbf64185c filtering azimuth 2022-06-17 22:03:15 +02:00
Roberto Viola
34c748e130 approximation of latlon fixed 2022-06-17 19:22:13 +02:00
Roberto Viola
3892799cf0 billboard removed 2022-06-16 16:29:34 +02:00
Roberto Viola
c1caaee60b metrics improved 2022-06-16 16:28:49 +02:00
Roberto Viola
aa5eab57c5 gpx track fixed 2022-06-15 16:56:09 +02:00
Roberto Viola
f91ddd85da gpx track added (not tested) 2022-06-15 16:31:46 +02:00
Roberto Viola
91d37a53dd added icons to metrics 2022-06-15 14:57:59 +02:00
Roberto Viola
d5f0c03334 fixed issues with metrics 2022-06-15 14:39:42 +02:00
Roberto Viola
39fcda7367 added metrics to cesium map 2022-06-15 11:53:44 +02:00
Roberto Viola
c86ee541f1 Merge branch 'Cesium' of https://github.com/cagnulein/qdomyos-zwift into Cesium 2022-06-15 11:08:16 +02:00
Roberto Viola
949dd12526 Merge branch 'master' into Cesium 2022-06-15 09:59:14 +02:00
Roberto Viola
fed03b83a4 Merge branch 'master' into Cesium 2022-06-15 09:58:25 +02:00
Roberto Viola
687cc9c900 fixing var error 2022-06-13 16:52:20 +02:00
Roberto Viola
3fe54e10f5 using crosshair image instead of the box 2022-06-13 16:37:58 +02:00
Roberto Viola
67d6fb0bc3 add a purple box as indicator of the user 2022-06-13 16:10:04 +02:00
Roberto Viola
658ef4c628 fixing build error 2022-06-13 12:41:04 +02:00
Roberto Viola
81842afc22 replaced flyto with lookat 2022-06-13 10:20:15 +02:00
Roberto Viola
673a6d9868 fixing strange azimuth 2022-06-11 23:03:19 +02:00
Roberto Viola
865815fca2 first version working! 2022-06-11 17:32:47 +02:00
27 changed files with 6769 additions and 85 deletions

1
.gitignore vendored
View File

@@ -44,3 +44,4 @@ template-examples/train-program-saver/debug.js
*.pro.user
*build-*
!build-qdomyos-zwift-Qt_*_for_iOS-Debug # Needed for Apple Watch
src/inner_templates/googlemaps/cesium-key.js

View File

@@ -54,8 +54,10 @@
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/qdomyoszwift">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "876E4E102594747F00BD5714"
@@ -63,7 +65,7 @@
BlueprintName = "watchkit"
ReferencedContainer = "container:qdomyoszwift.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</RemoteRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -71,8 +73,10 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/qdomyoszwift">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "876E4E102594747F00BD5714"
@@ -80,7 +84,16 @@
BlueprintName = "watchkit"
ReferencedContainer = "container:qdomyoszwift.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "876E4E102594747F00BD5714"
BuildableName = "watchkit.app"
BlueprintName = "watchkit"
ReferencedContainer = "container:qdomyoszwift.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

View File

@@ -12,11 +12,12 @@ ColumnLayout {
anchors.fill: parent
Settings {
id: settings
property string maps_type: "3D"
}
WebView {
id: webView
anchors.fill: parent
url: "http://localhost:" + settings.value("template_inner_QZWS_port") + "/googlemaps/maps.htm"
url: "http://localhost:" + settings.value("template_inner_QZWS_port") + "/" + (settings.value("maps_type") === "3D" ? "googlemaps" : "maps2d") + "/maps.htm"
visible: true
onLoadingChanged: {
if (loadRequest.errorString)

View File

@@ -1,5 +1,6 @@
#include "bluetoothdevice.h"
#include <QFile>
#include <QSettings>
#include <QTime>
@@ -122,9 +123,9 @@ double bluetoothdevice::difficult() { return m_difficult; }
void bluetoothdevice::cadenceSensor(uint8_t cadence) { Q_UNUSED(cadence) }
void bluetoothdevice::powerSensor(uint16_t power) { Q_UNUSED(power) }
void bluetoothdevice::speedSensor(double speed) { Q_UNUSED(speed) }
void bluetoothdevice::instantaneousStrideLengthSensor(double length) {Q_UNUSED(length);}
void bluetoothdevice::groundContactSensor(double groundContact) {Q_UNUSED(groundContact);}
void bluetoothdevice::verticalOscillationSensor(double verticalOscillation) {Q_UNUSED(verticalOscillation);}
void bluetoothdevice::instantaneousStrideLengthSensor(double length) { Q_UNUSED(length); }
void bluetoothdevice::groundContactSensor(double groundContact) { Q_UNUSED(groundContact); }
void bluetoothdevice::verticalOscillationSensor(double verticalOscillation) { Q_UNUSED(verticalOscillation); }
double bluetoothdevice::calculateMETS() { return ((0.048 * m_watt.value()) + 1.19); }
@@ -345,16 +346,19 @@ uint8_t bluetoothdevice::metrics_override_heartrate() {
return currentHeart().value();
}
void bluetoothdevice::changeGeoPosition(QGeoCoordinate p, double azimuth) {
void bluetoothdevice::changeGeoPosition(QGeoCoordinate p, double azimuth, double avgAzimuthNext300Meters) {
coordinateTS = QDateTime::currentMSecsSinceEpoch();
coordinateOdometer = odometer();
coordinate = p;
this->setAverageAzimuthNext300m(avgAzimuthNext300Meters);
this->azimuth = azimuth;
}
QGeoCoordinate bluetoothdevice::currentCordinate() {
if (coordinateTS) {
double distance = currentSpeed().value() * ((QDateTime::currentMSecsSinceEpoch() - coordinateTS) / 3600.0);
double distance = odometer() - coordinateOdometer;
QGeoCoordinate c = coordinate.atDistanceAndAzimuth(distance, this->azimuth);
qDebug() << "currentCordinate" << c << distance << currentSpeed().value();
c.setAltitude(coordinate.altitude());
// qDebug() << "currentCordinate" << c << distance << currentSpeed().value();
return c;
}
return coordinate;
@@ -362,3 +366,12 @@ QGeoCoordinate bluetoothdevice::currentCordinate() {
void bluetoothdevice::workoutEventStateChanged(bluetoothdevice::WORKOUT_EVENT_STATE state) { lastState = state; }
void bluetoothdevice::setInclination(double inclination) { Inclination = inclination; }
void bluetoothdevice::setGPXFile(QString filename) {
gpxFileName = filename;
QFile input(filename);
if (input.open(QIODevice::ReadOnly)) {
QByteArray asSaved = input.readAll();
gpxBase64 = "data:@file/xml;base64," + asSaved.toBase64();
input.close();
}
}

View File

@@ -25,6 +25,12 @@
#define SAME_BLUETOOTH_DEVICE(d1, d2) (d1.address() == d2.address())
#endif
class MetersByInclination {
public:
double meters;
double inclination;
};
class bluetoothdevice : public QObject {
Q_OBJECT
@@ -50,7 +56,10 @@ class bluetoothdevice : public QObject {
virtual metric currentCadence();
virtual double currentCrankRevolutions();
virtual QGeoCoordinate currentCordinate();
virtual double currentAzimuth() {qDebug() << azimuth; return azimuth;}
virtual double currentAzimuth() { return azimuth; }
virtual QList<MetersByInclination> nextInclination300Meters() { return NextInclination300Meters; }
virtual double averageAzimuthNext300m() { return azimuthAvgNext300m; }
virtual void setAverageAzimuthNext300m(double azimuth) { azimuthAvgNext300m = azimuth; }
virtual uint16_t lastCrankEventTime();
virtual void *VirtualDevice();
uint16_t watts(double weight);
@@ -72,6 +81,8 @@ class bluetoothdevice : public QObject {
metric currentMETS() { return METS; }
metric currentHeartZone() { return HeartZone; }
metric currentPowerZone() { return PowerZone; }
void setGPXFile(QString filename);
QString currentGPXBase64() { return gpxBase64; }
// in the future these 2 should be calculated inside the update_metrics()
void setHeartZone(double hz) { HeartZone = hz; }
@@ -95,11 +106,12 @@ class bluetoothdevice : public QObject {
virtual void changeResistance(int8_t res);
virtual void changePower(int32_t power);
virtual void changeInclination(double grade, double percentage);
virtual void changeGeoPosition(QGeoCoordinate p, double azimuth);
virtual void changeGeoPosition(QGeoCoordinate p, double azimuth, double avgAzimuthNext300Meters);
virtual void workoutEventStateChanged(bluetoothdevice::WORKOUT_EVENT_STATE state);
virtual void instantaneousStrideLengthSensor(double length);
virtual void groundContactSensor(double groundContact);
virtual void verticalOscillationSensor(double verticalOscillation);
virtual void changeNextInclination300Meters(QList<MetersByInclination> i) { NextInclination300Meters = i; }
Q_SIGNALS:
void connectedAndDiscovered();
@@ -139,7 +151,12 @@ class bluetoothdevice : public QObject {
QGeoCoordinate coordinate;
double azimuth;
double azimuthAvgNext300m;
quint64 coordinateTS = 0;
double coordinateOdometer = 0;
QString gpxBase64 = "";
QString gpxFileName = "";
QList<MetersByInclination> NextInclination300Meters;
metric Inclination;
metric HeartZone;

File diff suppressed because it is too large Load Diff

View File

@@ -356,11 +356,21 @@ homeform::homeform(QQmlApplicationEngine *engine, bluetooth *bl) {
connect(pelotonHandler, &peloton::pzpLoginState, this, &homeform::pzpLoginState);
// copying bundles zwo files in the right path if necessesary
QDirIterator it(":/zwo/");
while (it.hasNext()) {
qDebug() << it.next() << it.fileName();
if (!QFile(getWritableAppDir() + it.fileName()).exists()) {
QFile::copy(":/zwo/" + it.fileName(), getWritableAppDir() + it.fileName());
QDirIterator itZwo(":/zwo/");
QDir().mkdir(getWritableAppDir() + "training/");
while (itZwo.hasNext()) {
qDebug() << itZwo.next() << itZwo.fileName();
if (!QFile(getWritableAppDir() + "training/" + itZwo.fileName()).exists()) {
QFile::copy(":/zwo/" + itZwo.fileName(), getWritableAppDir() + "training/" + itZwo.fileName());
}
}
QDirIterator itGpx(":/gpx/");
QDir().mkdir(getWritableAppDir() + "gpx/");
while (itGpx.hasNext()) {
qDebug() << itGpx.next() << itGpx.fileName();
if (!QFile(getWritableAppDir() + "gpx/" + itGpx.fileName()).exists()) {
QFile::copy(":/gpx/" + itGpx.fileName(), getWritableAppDir() + "gpx/" + itGpx.fileName());
}
}
@@ -571,6 +581,8 @@ void homeform::trainProgramSignals() {
&treadmill::changeSpeed);
disconnect(trainProgram, &trainprogram::changeInclination, ((treadmill *)bluetoothManager->device()),
&treadmill::changeInclination);
disconnect(trainProgram, &trainprogram::changeNextInclination300Meters, bluetoothManager->device(),
&bluetoothdevice::changeNextInclination300Meters);
disconnect(trainProgram, &trainprogram::changeInclination, ((bike *)bluetoothManager->device()),
&bike::changeInclination);
disconnect(trainProgram, &trainprogram::changeFanSpeed, ((treadmill *)bluetoothManager->device()),
@@ -640,6 +652,8 @@ void homeform::trainProgramSignals() {
} else if (bluetoothManager->device()->deviceType() == bluetoothdevice::ROWING)
connect(trainProgram, &trainprogram::changePower, ((rower *)bluetoothManager->device()),
&rower::changePower);
connect(trainProgram, &trainprogram::changeNextInclination300Meters, bluetoothManager->device(),
&bluetoothdevice::changeNextInclination300Meters);
connect(((treadmill *)bluetoothManager->device()), &treadmill::tapeStarted, trainProgram,
&trainprogram::onTapeStarted);
connect(((bike *)bluetoothManager->device()), &bike::bikeStarted, trainProgram, &trainprogram::onTapeStarted);
@@ -3423,6 +3437,8 @@ void homeform::gpx_open_clicked(const QUrl &fileName) {
gpx g;
QList<trainrow> list;
auto g_list = g.open(file.fileName());
if (bluetoothManager->device())
bluetoothManager->device()->setGPXFile(file.fileName());
gpx_altitude_point_for_treadmill last;
quint32 i = 0;
list.reserve(g_list.size() + 1);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
Cesium.Ion.defaultAccessToken = '';

View File

@@ -0,0 +1,94 @@
var inclinationArray = []
const range = ({from = 0, to, step = 1, length = Math.ceil((to - from) / step)}) =>
Array.from({length}, (_, i) => from + i * step)
let canvasChart = {}
$(window).on('load', function () {
const data = {
labels: range({to: 300}),
datasets: [{
label: '',
data: inclinationArray,
fill: true,
tension: 0.2,
pointRadius: 0,
}]
};
let config = {
type: 'line',
data: data,
options: {
plugins: {
legend : {
display: false,
}
},
maintainAspectRatio: false,
responsive: true,
scales: {
x: {
min: 0,
max: 3000,
ticks: {
callback: () => ('')
},
grid: {
display: false,
drawOnChartArea:false,
drawBorder: false, // <-- this removes y-axis line
}
},
y: {
ticks: {
callback: () => ('')
},
grid: {
display: false,
drawOnChartArea:false,
drawBorder: false, // <-- this removes y-axis line
}
},
}
}
};
let ctx = document.getElementById('canvasChart').getContext('2d');
canvasChart = new Chart(ctx, config);
canvasChart.canvas.parentNode.style.height = '75px';
canvasChart.canvas.parentNode.style.width = '150px';
setTimeout(chartRefresh, 500);
});
function chartRefresh() {
onWorkout = false;
let el = new MainWSQueueElement({
msg: 'getnextinclination'
}, function(msg) {
if (msg.msg === 'R_getnextinclination') {
return msg.content;
}
return null;
}, 15000, 3);
el.enqueue().then(process_nextinclination).catch(function(err) {
console.error('Error is ' + err);
});
}
function process_nextinclination(msg) {
let arr = msg.split(",");
let lastValue = 0
inclinationArray = [];
for(var i=0; i<arr.length/2.0;i++) {
for(var ii=0; ii<parseFloat(arr[i*2]);ii++) {
lastValue += parseFloat(arr[(i*2)+1])
inclinationArray.push(lastValue);
}
}
canvasChart.data.datasets[0].data = inclinationArray;
canvasChart.update();
setTimeout(chartRefresh, 500);
}

View File

@@ -0,0 +1,8 @@
/*!
* chartjs-adapter-moment v1.0.0
* https://www.chartjs.org
* (c) 2021 chartjs-adapter-moment Contributors
* Released under the MIT license
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("moment"),require("chart.js")):"function"==typeof define&&define.amd?define(["moment","chart.js"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).moment,e.Chart)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=n(e);const a={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};t._adapters._date.override("function"==typeof f.default?{_id:"moment",formats:function(){return a},parse:function(e,t){return"string"==typeof e&&"string"==typeof t?e=f.default(e,t):e instanceof f.default||(e=f.default(e)),e.isValid()?e.valueOf():null},format:function(e,t){return f.default(e).format(t)},add:function(e,t,n){return f.default(e).add(t,n).valueOf()},diff:function(e,t,n){return f.default(e).diff(f.default(t),n)},startOf:function(e,t,n){return e=f.default(e),"isoWeek"===t?(n=Math.trunc(Math.min(Math.max(0,n),6)),e.isoWeekday(n).startOf("day").valueOf()):e.startOf(t).valueOf()},endOf:function(e,t){return f.default(e).endOf(t).valueOf()}}:{})}));
//# sourceMappingURL=chartjs-adapter-moment.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,26 +1,87 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<style>
.map {
height: 1000px;
width: 100%;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="globals.js"></script>
<script src="main_ws_manager.js"></script>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<title>OpenLayers example</title>
</head>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Include the CesiumJS JavaScript and CSS files -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.94/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.94/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<script src="jquery-3.6.0.min.js"></script>
<script src="chartjs.3.4.1.min.js"></script>
<script src="chart.js"></script>
<script src="globals.js"></script>
<script src="bike.js"></script>
<script src="main_ws_manager.js"></script>
<script src="cesium-key.js"></script>
<style>
canvas{
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
</style>
</head>
<body>
<div id="cesiumContainer" style="display: block;
position: absolute;
top: 0;
left: 0;
border: none;
width: 100%;
height: 100%;"></div>
<script>
// Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain()
});
$('.cesium-viewer-animationContainer').css("visibility", "hidden");
$('.cesium-viewer-bottom').css("visibility", "hidden");
$('.cesium-viewer-timelineContainer').css("visibility", "hidden");
$('.cesium-viewer-fullscreenContainer').css("visibility", "hidden");
viewer.scene.globe.enableLighting = true;
viewer.scene.fog.enabled = true;
viewer.scene.globe.depthTestAgainstTerrain = true;
// Add Cesium OSM Buildings, a global 3D buildings layer.
//const buildingTileset = viewer.scene.primitives.add(Cesium.createOsmBuildings());
var center = Cesium.Cartesian3.fromDegrees(10.855092, 44.508273, 180);
var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
let map = viewer;
viewer.scene.camera.lookAtTransform(transform, new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), -Math.PI/8, 100));
var bike = viewer.scene.primitives.add(
Cesium.ModelExperimental.fromGltf({
url: bikeUri,
minimumPixelSize: 48,
scale: 1,
scene : viewer.scene,
color: Cesium.Color.ORANGE,
silhouetteColor: Cesium.Color.ORANGE,
silhouetteSize: 2,
})
);
</script>
</div>
</body>
<body>
<div id="map" class="map"></div>
<div id="cesiumContainer" class="cesiumContainer"></div>
<div><p class="metrics" style="color: #FFFFFF; position: absolute; top: 80px; right: 0;">Speed: --<br>Cadence: --</p></div>
<div style="background-color:white; border: 0px solid #aaa; border-radius: 10px; overflow: hidden; position:absolute; bottom: 0px; right: 0; width=150px; height=75px"><canvas id="canvasChart" style="width=150px; height=75px"></canvas></div>
<script type="text/javascript">
let cameraComplete = true
let lastAzimuth = 0
let lastAlt = 0
let lastLat = 0
let lastLon = 0
let refreshRate = 25
let currentSpeed = 0
let lastCoordinateTS = 0
let altOffset = 0
function a() {
let lat = 0
let lon = 0
onWorkout = false;
let el = new MainWSQueueElement({
msg: 'getlatlon'
}, function(msg) {
@@ -32,46 +93,243 @@
el.enqueue().then(process_latlon).catch(function(err) {
console.error('Error is ' + err);
});
keys_arr = ['speed', 'cadence', 'heart', 'calories', 'distance', 'watts', 'elapsed_h', 'elapsed_m', 'elapsed_s', 'inclination', 'resistance', 'elevation', 'altitude']
let ell = new MainWSQueueElement({
msg: null,
}, function(msg) {
if (msg.msg === 'workout') {
var speed = 0;
var cadence = 0;
var hr = 0;
var calories = 0;
var odometer = 0;
var watt = 0;
var elapsed_h = 0;
var elapsed_m = 0;
var elapsed_s = 0;
var inclination = 0;
var resistance = 0;
var elevation = 0;
var altitude = 0;
for (let key of keys_arr) {
if (msg.content[key] === undefined)
continue;
if (key === 'speed') {
currentSpeed = speed = msg.content[key];
} else if (key === 'cadence') {
cadence = msg.content[key];
} else if (key === 'heart') {
hr = msg.content[key];
} else if (key === 'calories') {
calories = msg.content[key];
} else if (key === 'distance') {
odometer = msg.content[key];
} else if (key === 'watts') {
watt = msg.content[key];
} else if (key === 'elapsed_h') {
elapsed_h = msg.content[key];
} else if (key === 'elapsed_m') {
elapsed_m = msg.content[key];
} else if (key === 'elapsed_s') {
elapsed_s = msg.content[key];
} else if (key === 'inclination') {
inclination = msg.content[key];
} else if (key === 'resistance') {
resistance = msg.content[key];
} else if (key === 'elevation') {
elevation = msg.content[key];
} else if (key === 'altitude') {
altitude = msg.content[key];
}
}
$('.metrics').html("🏃Speed: " + speed.toFixed(2) + "<br>🚴Cadence:" + cadence.toFixed(0) + "<br>💓Heart:"+ hr.toFixed(0) + "<br>🔥Calories:"+ calories.toFixed(1) + "<br>📏Odometer:"+ odometer.toFixed(2) + "<br>⚡Watt:"+ watt.toFixed(0) + "<br>⏲Elapsed:"+ elapsed_h.toString().padStart(2, "0") + ":" + elapsed_m.toString().padStart(2, "0") + ":"+ elapsed_s.toString().padStart(2, "0") + "<br>📐Inclination:"+ inclination.toFixed(1) + "<br>🧲Resistance:"+ resistance.toFixed(0) + "<br>✈Altitude:"+ altitude.toFixed(1) + "<br>⛰Elevation:"+ elevation.toFixed(2));
}
return null;
}, 15000, 3);
ell.enqueue().then(onWorkout).catch(function(err) {
console.error('Error is ' + err);
});
}
function camera_complete() {
cameraComplete = true;
}
function easing_function(time) {
return time;
}
function process_latlon(msg) {
/*if(!cameraComplete) return;*/
cameraComplete = false;
let lat = parseFloat(msg.split(",")[0]);
let lon = parseFloat(msg.split(",")[1]);
map.getView().animate({center: ol.proj.fromLonLat([lon, lat])});
markers.getSource().getFeatures()[0].getGeometry().setCoordinates(ol.proj.fromLonLat([lon, lat]));
setTimeout(a,2000);
let alt = parseFloat(msg.split(",")[2]) + altOffset;
let realAzimuth = parseFloat(msg.split(",")[3]);
let azimuth = parseFloat(msg.split(",")[4]);
let realLat = lat
let realLon = lon
if(lastAzimuth != azimuth) {
if((lastAzimuth > azimuth && Math.abs(lastAzimuth - azimuth) < 180) ||
(lastAzimuth < azimuth && Math.abs(lastAzimuth - azimuth) > 180)) {
lastAzimuth = azimuth = lastAzimuth - 0.01;
if(azimuth < 0)
lastAzimuth = azimuth = 359.99;
}
else {
lastAzimuth = azimuth = lastAzimuth + 0.01;
if(azimuth >= 360)
lastAzimuth = azimuth = 0;
}
}
if(Math.abs(lastAzimuth - azimuth) < 0.01)
lastAzimuth = azimuth;
if(lastAlt != alt && Math.abs(lastAlt-alt) < 30) {
if(lastAlt > alt)
lastAlt = alt = lastAlt - 0.01;
else
lastAlt = alt = lastAlt + 0.01;
} else {
lastAlt = alt;
}
if(Math.abs(lastAlt - alt) < 0.01)
lastAlt = alt;
var p1 = Cesium.Cartesian3.fromDegrees(lon, lat, alt);
var p2 = Cesium.Cartesian3.fromDegrees(lastLon, lastLat, lastAlt);
var distance = Cesium.Cartesian3.distance(p1, p2);
var tick = (Date.now() - lastCoordinateTS)
let deltaT = (currentSpeed / 3.6) / (1000 / (tick - 1));
if(distance < 30)
deltaT = deltaT / 2;
lastCoordinateTS = Date.now()
deltaT = distance / deltaT;
console.log(Date.now() + " distance delta " + distance + " " + tick);
if(Math.abs(lat - lastLat) > 0.1 || Math.abs(lon - lastLon) > 0.1) {
lastLat = lat;
lastLon = lon;
lastCoordinateTS = Date.now()
} else if(lat != lastLat || lon != lastLon) {
if(lat > lastLat) {
lastLat = lat = lastLat + ((lat-lastLat) / deltaT);
} else {
lastLat = lat = lastLat - ((lastLat - lat) / deltaT);
}
if(lon > lastLon) {
lastLon = lon = lastLon + ((lon-lastLon) / deltaT);
} else {
lastLon = lon = lastLon - ((lastLon-lon) / deltaT);
}
}
let map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 16
})
});
markers = new ol.layer.Vector({
source: new ol.source.Vector(),
style: new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 0.5],
anchorXUnits: 'fraction',
anchorYUnits: 'fraction',
scale: 0.2,
src: 'marker.png'
})
})
});
map.addLayer(markers);
console.log("azimuth " + azimuth + " " + lat + " " + lon);
var center = Cesium.Cartesian3.fromDegrees(lon, lat, alt);
var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
viewer.scene.camera.lookAtTransform(transform, new Cesium.HeadingPitchRange(Cesium.Math.toRadians(azimuth), -Math.PI/8, 300));
let marker = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat([37.41, 8.82])));
markers.getSource().addFeature(marker);
const length = 2;
const startLon = Cesium.Math.toRadians(realLon);
const endLon = Cesium.Math.toRadians(realLon + 0.000001);
const startLat = Cesium.Math.toRadians(realLat);
const terrainSamplePositions = [];
for (let i = 0; i < length; ++i) {
const lonL = Cesium.Math.lerp(
endLon,
startLon,
i / (length - 1)
);
const position = new Cesium.Cartographic(lonL, startLat);
terrainSamplePositions.push(position);
}
Promise.resolve(
Cesium.sampleTerrainMostDetailed(
viewer.terrainProvider,
terrainSamplePositions
)
).then(function (samples) {
const hpr = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(realAzimuth),
0,
0
);
bike.modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(Cesium.Cartesian3.fromDegrees(realLon, realLat, samples[0].height), hpr);
});
const startLonCamera = Cesium.Math.toRadians(Cesium.Math.toDegrees(viewer.scene.camera.positionCartographic.longitude));
const endLonCamera = Cesium.Math.toRadians(Cesium.Math.toDegrees(viewer.scene.camera.positionCartographic.longitude) + 0.000001);
const startLatCamera = Cesium.Math.toRadians(Cesium.Math.toDegrees(viewer.scene.camera.positionCartographic.latitude));
const terrainSamplePositionsCamera = [];
for (let i = 0; i < length; ++i) {
const lonLCamera = Cesium.Math.lerp(
endLonCamera,
startLonCamera,
i / (length - 1)
);
const positionCamera = new Cesium.Cartographic(lonLCamera, startLatCamera);
terrainSamplePositionsCamera.push(positionCamera);
}
Promise.resolve(
Cesium.sampleTerrainMostDetailed(
viewer.terrainProvider,
terrainSamplePositionsCamera
)
).then(function (samples) {
let terrain_height = samples[0].height;
let camera_height = viewer.scene.camera.positionCartographic.height;
//Determine terrain relative height (edist)
let edist = camera_height - terrain_height;
//console.log(camera_height + " " + terrain_height)
//Unburrow code
if (edist - altOffset < 50) {
altOffset = 70 - edist
} else {
altOffset = 0;
}
});
setTimeout(a,refreshRate);
/*
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(lon, lat, alt + 60),
duration: 2,
orientation : {
heading : Cesium.Math.toRadians(azimuth),
pitch : Cesium.Math.toRadians(-5.0),
},
easingFunction: easing_function,
complete: camera_complete
});*/
}
function process_gpxbase64(msg) {
viewer.dataSources
.add(
Cesium.GpxDataSource.load(
msg,
{
clampToGround: true,
}
))
}
let el = new MainWSQueueElement({
msg: 'getgpxbase64'
}, function(msg) {
if (msg.msg === 'R_getgpxbase64') {
return msg.content;
}
return null;
}, 15000, 3);
el.enqueue().then(process_gpxbase64).catch(function(err) {
console.error('Error is ' + err);
});
setTimeout(a,0);
</script>
</body>

View File

@@ -0,0 +1,9 @@
const host_url = (!location.host || location.host.length == 0)?'192.168.25.24:7666':location.host;
function get_template_name() {
let splits = location.pathname.split('/');
if (splits.length>=2)
return splits[splits.length - 2];
else
return '';
}

View File

@@ -0,0 +1,132 @@
let main_ws = null;
let main_ws_queue = [];
class MainWSQueueElement {
constructor(msg_to_send, _inner_process, timeout, retry_num) {
this.msg_to_send = msg_to_send;
this.needs_to_send = msg_to_send != null;
this.timeout = timeout || 5000;
this.retry_num = retry_num || 1;
this.timer = null;
this.resolve = null;
this.reject = null;
this._inner_process = _inner_process;
}
inner_process_msg(msg) {
if (this._inner_process)
return this._inner_process(msg);
else
return {};
}
process_arrived_msg(msg) {
let out = this.inner_process_msg(msg);
if (out) {
if (this.timer !==null) {
clearTimeout(this.timer);
this.timer = null;
}
if (this.resolve)
setTimeout(function() { this.resolve(out); }.bind(this), 0);
}
return out;
}
enqueue() {
main_ws_enqueue(this);
return new Promise(function(resolve, reject) {
this.resolve = resolve;
this.reject = reject;
}.bind(this));
}
pop_msg_to_send() {
if (this.needs_to_send) {
this.needs_to_send = false;
if (this.retry_num < 0 || this.retry_num > 0)
this.timer = setTimeout(function() {
this.timer = null;
if (this.retry_num == 0) {
main_ws_dequeue(this);
if (this.reject) {
this.reject(new Error('Timeout error detected'));
}
}
else {
this.needs_to_send = true;
main_ws_enqueue();
}
}.bind(this), this.timeout);
if (this.retry_num > 0)
this.retry_num--;
return this.msg_to_send;
}
else
return null;
}
}
function main_ws_dequeue(el) {
let idx = main_ws_queue.indexOf(el);
if (idx >= 0) {
main_ws_queue.splice(idx, 1);
}
}
function main_ws_enqueue(el) {
if (el)
main_ws_queue.push(el);
if (main_ws)
main_ws_queue_process();
}
function main_ws_queue_process(msg) {
if (!main_ws)
return;
let jsonobj;
for (let i = 0; i < main_ws_queue.length; i++) {
let el = main_ws_queue[i];
if ((jsonobj = el.pop_msg_to_send())) {
let logString = JSON.stringify(jsonobj);
main_ws.send(logString);
console.log('WS >> ' + logString);
}
else if (msg) {
if (el.process_arrived_msg(msg)) {
main_ws_queue.splice(i, 1);
i--;
msg = null;
}
}
}
}
function main_ws_connect() {
let socket = new WebSocket((location.protocol == 'https:'?'wss://' : 'ws://') + host_url + '/' + get_template_name() + '-ws');
socket.onopen = function (event) {
console.log('Upgrade HTTP connection OK');
main_ws = socket;
main_ws_queue_process();
};
socket.onclose = function(e) {
main_ws = null;
console.log('Socket is closed. Reconnect will be attempted in 30 second.', e.reason);
setTimeout(function() {
main_ws_connect();
}, 5000);
};
socket.onerror = function(err) {
main_ws = null;
console.error('Socket encountered error: ', err.message, 'Closing socket');
socket.close();
};
socket.onmessage = function (event) {
console.log(event.data);
let msg = JSON.parse(event.data);
main_ws_queue_process(msg);
};
}
main_ws_connect();

View File

@@ -0,0 +1,78 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<style>
.map {
height: 1000px;
width: 100%;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="globals.js"></script>
<script src="main_ws_manager.js"></script>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<title>OpenLayers example</title>
</head>
<body>
<div id="map" class="map"></div>
<script type="text/javascript">
function a() {
let lat = 0
let lon = 0
let el = new MainWSQueueElement({
msg: 'getlatlon'
}, function(msg) {
if (msg.msg === 'R_getlatlon') {
return msg.content;
}
return null;
}, 15000, 3);
el.enqueue().then(process_latlon).catch(function(err) {
console.error('Error is ' + err);
});
}
function process_latlon(msg) {
let lat = parseFloat(msg.split(",")[0]);
let lon = parseFloat(msg.split(",")[1]);
map.getView().animate({center: ol.proj.fromLonLat([lon, lat])});
markers.getSource().getFeatures()[0].getGeometry().setCoordinates(ol.proj.fromLonLat([lon, lat]));
setTimeout(a,2000);
}
let map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 16
})
});
markers = new ol.layer.Vector({
source: new ol.source.Vector(),
style: new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 0.5],
anchorXUnits: 'fraction',
anchorYUnits: 'fraction',
scale: 0.2,
src: 'marker.png'
})
})
});
map.addLayer(markers);
let marker = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat([37.41, 8.82])));
markers.getSource().addFeature(marker);
setTimeout(a,0);
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -512,7 +512,7 @@ ApplicationWindow {
}
ItemDelegate {
id: gpx_open
text: qsTr("Open GPX")
text: qsTr("🗺️ Open GPX")
width: parent.width
onClicked: {
fileDialogGPX.visible = true
@@ -521,7 +521,7 @@ ApplicationWindow {
}
ItemDelegate {
id: trainprogram_open
text: qsTr("Open Train Program")
text: qsTr("📈 Open Train Program")
width: parent.width
onClicked: {
stackView.push("TrainingProgramsList.qml")
@@ -606,7 +606,7 @@ ApplicationWindow {
FileDialog {
id: fileDialogGPX
title: "Please choose a file"
folder: shortcuts.home
folder: "file://" + rootItem.getWritableAppDir() + 'gpx'
onAccepted: {
console.log("You chose: " + fileDialogGPX.fileUrl)
gpx_open_clicked(fileDialogGPX.fileUrl)

View File

@@ -53,6 +53,8 @@
<file>inner_templates/googlemaps/marker.png</file>
<file>settings-tts.qml</file>
<file>NewPageElement.qml</file>
<file>inner_templates/googlemaps/jquery-3.6.0.min.js</file>
<file>inner_templates/googlemaps/bike.js</file>
<file>zwo/Easy intervals.zwo</file>
<file>zwo/HIIT Aerobic.zwo</file>
<file>zwo/HIIT Anaerobic Sprint.zwo</file>
@@ -64,5 +66,15 @@
<file>zwo/Tempo and endurance.zwo</file>
<file>zwo/VO2max with recovery.zwo</file>
<file>zwo/VO2max with sweet spot.zwo</file>
<file>inner_templates/googlemaps/chart.js</file>
<file>inner_templates/googlemaps/chartjs.3.4.1.min.js</file>
<file>inner_templates/googlemaps/chartjs-adapter-moment.js</file>
<file>inner_templates/googlemaps/chartjs-plugin-annotation.min.js</file>
<file>inner_templates/googlemaps/cesium-key.js</file>
<file>inner_templates/maps2d/globals.js</file>
<file>inner_templates/maps2d/main_ws_manager.js</file>
<file>inner_templates/maps2d/maps.htm</file>
<file>inner_templates/maps2d/marker.png</file>
<file>gpx/Italy - Passo dello Stelvio.gpx</file>
</qresource>
</RCC>

View File

@@ -442,6 +442,9 @@ import Qt.labs.settings 1.0
property bool tile_vertical_oscillation_enabled: false
property int tile_vertical_oscillation_order: 34
property string sex: "Male"
// from the version 2.10.111
property string maps_type: "3D"
}
function paddingZeros(text, limit) {
@@ -6067,6 +6070,46 @@ import Qt.labs.settings 1.0
accordionContent: "settings-tts.qml"
}
AccordionElement {
id: mapsAccordion
title: qsTr("Maps 🗺️")
indicatRectColor: Material.color(Material.Grey)
textColor: Material.color(Material.Grey)
color: Material.backgroundColor
//width: 640
//anchors.top: acc1.bottom
//anchors.topMargin: 10
accordionContent: ColumnLayout {
spacing: 0
RowLayout {
spacing: 10
Label {
id: labelMapsType
text: qsTr("Maps Type:")
Layout.fillWidth: true
}
ComboBox {
id: mapsTypeTextField
model: [ "2D", "3D" ]
displayText: settings.maps_type
Layout.fillHeight: false
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onActivated: {
console.log("combomodel activated" + mapsTypeTextField.currentIndex)
displayText = mapsTypeTextField.currentValue
}
}
Button {
id: okMapsType
text: "OK"
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
onClicked: settings.maps_type = mapsTypeTextField.displayText
}
}
}
}
AccordionElement {
id: experimentalFeatureAccordion
title: qsTr("Experimental Features")

View File

@@ -476,19 +476,45 @@ void TemplateInfoSenderBuilder::onGetSessionArray(TemplateInfoSender *tempSender
tempSender->send(out.toJson());
}
void TemplateInfoSenderBuilder::onGetGPXBase64(TemplateInfoSender *tempSender) {
if (!device)
return;
QJsonObject main;
main[QStringLiteral("content")] = device->currentGPXBase64();
main[QStringLiteral("msg")] = QStringLiteral("R_getgpxbase64");
QJsonDocument out(main);
tempSender->send(out.toJson());
}
void TemplateInfoSenderBuilder::onGetLatLon(TemplateInfoSender *tempSender) {
if (!device)
return;
QJsonObject main;
main[QStringLiteral("content")] = QString::number(device->currentCordinate().latitude()) + "," +
QString::number(device->currentCordinate().longitude()) + "," +
QString::number(device->currentCordinate().altitude()) + "," +
QString::number(device->currentAzimuth());
main[QStringLiteral("content")] = QString::number(device->currentCordinate().latitude(), 'g', 9) + "," +
QString::number(device->currentCordinate().longitude(), 'g', 9) + "," +
QString::number(device->currentCordinate().altitude(), 'g', 9) + "," +
QString::number(device->currentAzimuth(), 'g', 9) + "," +
QString::number(device->averageAzimuthNext300m());
main[QStringLiteral("msg")] = QStringLiteral("R_getlatlon");
QJsonDocument out(main);
tempSender->send(out.toJson());
}
void TemplateInfoSenderBuilder::onNextInclination300Meters(TemplateInfoSender *tempSender) {
if (!device)
return;
QJsonObject main;
QList<MetersByInclination> ii = device->nextInclination300Meters();
QString values = "";
for (int i = 0; i < ii.length(); i++) {
values += QString::number(ii.at(i).meters, 'g', 0) + "," + QString::number(ii.at(i).inclination, 'g', 1) + ",";
}
main[QStringLiteral("content")] = values;
main[QStringLiteral("msg")] = QStringLiteral("R_getnextinclination");
QJsonDocument out(main);
tempSender->send(out.toJson());
}
void TemplateInfoSenderBuilder::onStart(TemplateInfoSender *tempSender) {
if (!device->isPaused()) {
device->clearStats();
@@ -647,6 +673,12 @@ void TemplateInfoSenderBuilder::onDataReceived(const QByteArray &data) {
} else if (msg == QStringLiteral("getlatlon")) {
onGetLatLon(sender);
return;
} else if (msg == QStringLiteral("getnextinclination")) {
onNextInclination300Meters(sender);
return;
} else if (msg == QStringLiteral("getgpxbase64")) {
onGetGPXBase64(sender);
return;
} else if (msg == QStringLiteral("setresistance")) {
onSetResistance(jsonObject[QStringLiteral("content")], sender);
return;
@@ -699,7 +731,7 @@ void TemplateInfoSenderBuilder::onDataReceived(const QByteArray &data) {
}
}
}
qDebug() << QStringLiteral("Unrecognized message") << data;
// qDebug() << QStringLiteral("Unrecognized message") << data;
}
void TemplateInfoSenderBuilder::buildContext(bool forceReinit) {
@@ -799,6 +831,7 @@ void TemplateInfoSenderBuilder::buildContext(bool forceReinit) {
obj.setProperty(QStringLiteral("instructorName"), instructorName);
obj.setProperty(QStringLiteral("latitude"), device->currentCordinate().latitude());
obj.setProperty(QStringLiteral("longitude"), device->currentCordinate().longitude());
obj.setProperty(QStringLiteral("altitude"), device->currentCordinate().altitude());
obj.setProperty(
QStringLiteral("nickName"),
(nickName = settings.value(QStringLiteral("user_nickname"), QStringLiteral("")).toString()).isEmpty()

View File

@@ -59,6 +59,8 @@ class TemplateInfoSenderBuilder : public QObject {
void onAppendActivityDescription(const QJsonValue &msgContent, TemplateInfoSender *tempSender);
void onGetSessionArray(TemplateInfoSender *tempSender);
void onGetLatLon(TemplateInfoSender *tempSender);
void onNextInclination300Meters(TemplateInfoSender *tempSender);
void onGetGPXBase64(TemplateInfoSender *tempSender);
void onStart(TemplateInfoSender *tempSender);
void onPause(TemplateInfoSender *tempSender);
void onStop(TemplateInfoSender *tempSender);

View File

@@ -79,6 +79,74 @@ double trainprogram::calculateDistanceForRow(int32_t row) {
return rows.at(row).distance;
}
// meters, inclination
QList<MetersByInclination> trainprogram::inclinationNext300Meters() {
int c = currentStep;
double km = 0;
QList<MetersByInclination> next300;
while (1) {
if (c < rows.length()) {
if (km > 0.3) {
return next300;
}
MetersByInclination p;
p.meters = rows.at(c).distance * 1000.0;
p.inclination = rows.at(c).inclination;
next300.append(p);
km += rows.at(c).distance;
} else {
return next300;
}
c++;
}
return next300;
}
double trainprogram::avgAzimuthNext300Meters() {
int c = currentStep;
double km = 0;
double sinTotal = 0;
double cosTotal = 0;
if (rows.at(c).latitude != 0 || rows.at(c).longitude != 0) {
while (1) {
if (c < rows.length()) {
if (km > 0.3) {
double averageDirection = atan(sinTotal / cosTotal) * (180 / M_PI);
if (cosTotal < 0) {
averageDirection += 180;
} else if (sinTotal < 0) {
averageDirection += 360;
}
return averageDirection;
}
for (double i = 0; i < rows.at(c).distance; i += 0.001) {
sinTotal += sin(rows.at(c).azimuth * (M_PI / 180));
cosTotal += cos(rows.at(c).azimuth * (M_PI / 180));
}
km += rows.at(c).distance;
} else {
double averageDirection = atan(sinTotal / cosTotal) * (180 / M_PI);
if (cosTotal < 0) {
averageDirection += 180;
} else if (sinTotal < 0) {
averageDirection += 360;
}
return averageDirection;
}
c++;
}
}
return 0;
}
void trainprogram::scheduler() {
QSettings settings;
@@ -104,6 +172,7 @@ void trainprogram::scheduler() {
if (rows.at(0).inclination != -200) {
qDebug() << QStringLiteral("trainprogram change inclination") + QString::number(rows.at(0).inclination);
emit changeInclination(rows.at(0).inclination, rows.at(0).inclination);
emit changeNextInclination300Meters(inclinationNext300Meters());
}
} else {
if (rows.at(0).resistance != -1) {
@@ -139,6 +208,7 @@ void trainprogram::scheduler() {
if (!((bike *)bluetoothManager->device())->inclinationAvailableByHardware())
bluetoothManager->device()->setInclination(rows.at(0).inclination);
emit changeInclination(rows.at(0).inclination, rows.at(0).inclination);
emit changeNextInclination300Meters(inclinationNext300Meters());
}
}
@@ -155,7 +225,7 @@ void trainprogram::scheduler() {
p.setAltitude(rows.at(0).altitude);
p.setLatitude(rows.at(0).latitude);
p.setLongitude(rows.at(0).longitude);
emit changeGeoPosition(p, rows.at(0).azimuth);
emit changeGeoPosition(p, rows.at(0).azimuth, avgAzimuthNext300Meters());
}
}
@@ -210,6 +280,7 @@ void trainprogram::scheduler() {
qDebug() << QStringLiteral("trainprogram change inclination ") +
QString::number(rows.at(currentStep).inclination);
emit changeInclination(rows.at(currentStep).inclination, rows.at(currentStep).inclination);
emit changeNextInclination300Meters(inclinationNext300Meters());
}
} else {
if (rows.at(currentStep).resistance != -1) {
@@ -251,6 +322,7 @@ void trainprogram::scheduler() {
if (!((bike *)bluetoothManager->device())->inclinationAvailableByHardware())
bluetoothManager->device()->setInclination(rows.at(currentStep).inclination);
emit changeInclination(rows.at(currentStep).inclination, rows.at(currentStep).inclination);
emit changeNextInclination300Meters(inclinationNext300Meters());
}
}
@@ -266,12 +338,20 @@ void trainprogram::scheduler() {
QString::number(rows.at(currentStep).latitude) + " " +
QString::number(rows.at(currentStep).longitude) + " " +
QString::number(rows.at(currentStep).altitude) + " " +
QString::number(rows.at(currentStep).distance) + " " +
QString::number(rows.at(currentStep).azimuth);
QGeoCoordinate p;
p.setAltitude(rows.at(currentStep).altitude);
p.setLatitude(rows.at(currentStep).latitude);
p.setLongitude(rows.at(currentStep).longitude);
emit changeGeoPosition(p, rows.at(currentStep).azimuth);
p.setAltitude(rows.at(currentStep).altitude);
// qDebug() << c << rows.at(currentStep+1).latitude << rows.at(currentStep + 1).longitude <<
// c.distanceTo(p) << rows.at(currentStep).distance;
if (bluetoothManager->device()->odometer() - lastOdometer > 0)
p = p.atDistanceAndAzimuth((bluetoothManager->device()->odometer() - lastOdometer),
rows.at(currentStep).azimuth);
emit changeGeoPosition(p, rows.at(currentStep).azimuth, avgAzimuthNext300Meters());
}
} else {
qDebug() << QStringLiteral("trainprogram ends!");

View File

@@ -89,14 +89,17 @@ class trainprogram : public QObject {
void changeSpeed(double speed);
bool changeFanSpeed(uint8_t speed);
void changeInclination(double grade, double inclination);
void changeNextInclination300Meters(QList<MetersByInclination>);
void changeResistance(int8_t resistance);
void changeRequestedPelotonResistance(int8_t resistance);
void changeCadence(int16_t cadence);
void changePower(int32_t power);
void changeSpeedAndInclination(double speed, double inclination);
void changeGeoPosition(QGeoCoordinate p, double azimuth);
void changeGeoPosition(QGeoCoordinate p, double azimuth, double avgAzimuthNext300Meters);
private:
double avgAzimuthNext300Meters();
QList<MetersByInclination> inclinationNext300Meters();
uint32_t calculateTimeForRow(int32_t row);
uint32_t calculateTimeForRowMergingRamps(int32_t row);
double calculateDistanceForRow(int32_t row);

View File

@@ -171,7 +171,7 @@ void WebServerInfoSender::processTextMessage(QString message) {
if (pClient) {
pClient->sendTextMessage(message);
}*/
qDebug() << QStringLiteral("Message received:") << message;
//qDebug() << QStringLiteral("Message received:") << message;
emit onDataReceived(message.toUtf8());
}
@@ -260,6 +260,6 @@ void WebServerInfoSender::processBinaryMessage(QByteArray message) {
if (pClient) {
pClient->sendBinaryMessage(message);
}*/
qDebug() << QStringLiteral("Binary Message received:") << message.toHex();
//qDebug() << QStringLiteral("Binary Message received:") << message.toHex();
emit onDataReceived(message);
}