In the previous post, I removed the contact-quality score from my Apple Watch hurling app. It was one of those ideas that looked convincing in the app, but I could not properly explain what the number meant. I decided to keep the measurements, let the player rate their own strikes, and stop trying to turn wrist data into a judgement about technique.
Since then, I have mostly gone in that direction. Instead of finding another formula for strike quality, I rebuilt how the Watch records motion, retrained the model to recognise more types of strike, and added tools for comparing practice sessions over time.
I also bought an Apple Watch Series 11.
The decision came after I watched Apple’s WWDC 2023 session “What’s new in Core Motion”. Apple used a baseball swing to demonstrate the new high-rate sensor API: 800 Hz acceleration to find impact, 200 Hz device motion to find the beginning of the swing, then the difference between those timestamps to calculate Time to Contact.
That example was very close to the problem I had been working on with hurling. It was the point where buying a newer Watch started to feel worthwhile rather than just nice to have.
My Series 6 was fine for the earlier experiments, but it could not record those two high-rate streams. The first recordings in this project were captured at 50 Hz, followed by later sessions at 100 Hz. Moving to the new Watch gave me a much better source for contact timing, although it did not suddenly make the data easy to interpret.
Two Sensor Streams With Different Jobs
The Watch does not record everything at 800 Hz. Each session contains two motion streams:
- Device motion at 200 Hz, including user acceleration, rotation rate, gravity, and Watch orientation.
- Raw acceleration at 800 Hz, which captures the short, sharp changes around contact.
The split is useful because the streams do different jobs. The CNN runs on the 200 Hz device-motion data. I also use that stream for movement metrics and the 3D visualisation. The 800 Hz acceleration is used to place contact more precisely and calculate timing. It gives me roughly one raw acceleration sample every 1.25 milliseconds around the impact.
Both streams have their own timestamps, so I align them by time rather than assuming that every device-motion sample has exactly four matching acceleration samples. Sensor batches can be delayed and recordings can contain small gaps. The timestamps keep those gaps visible.
I also moved away from CSV files. A session is now stored as two compact binary files and a metadata file inside an .mmrec package. The same package can transfer from the Watch to the iPhone, open in my analysis tool, and later be used for model training. I decided not to add a lower-frequency fallback for older Watches because I want new recordings to follow the same format.
From One Strike Label to Five Classes
The model from my earlier posts answered a simple question: strike or non-strike. That was enough to get detection working, but it was not enough to describe a full practice session.
The current model has five classes: non-strike, regular dominant strike, non-dominant strike, ground strike, and free-puck. I did not jump straight to five. First I retrained the model on the native 200 Hz data. Then I separated dominant and non-dominant strikes. Ground strikes came next, after reviewing individual attempts showed that they did not fit the same timing rules as self-toss air strikes. Free-puck was the final class I added.
Free-pucks needed more motion before contact than an ordinary self-toss strike. The current input window contains 160 samples, or about 0.8 seconds at 200 Hz, and more of that window sits before the prediction point. This lets the model see more of the preparation and lift instead of mostly seeing the final swing.
Fig 2 shows the latest training run. The graph on the left is training and validation loss. The one on the right is macro F1 across the five classes. I pay more attention to F1 than plain accuracy because the recordings contain far more background than actual strikes. The validation score finishes above 0.9, although it jumps around more than the training score because the validation set is much smaller.
The confusion matrix is easier to read than one overall accuracy number. In this validation split, the model found all 52 regular strikes, 83 non-dominant strikes, and 69 ground strikes. It found 143 of 150 free-pucks, while the other seven were classified as regular strikes.
There are more than 12,000 non-strike windows in the same split. If I only reported accuracy, that large background class would make the result look better than it really is. The smaller strike classes are the interesting part.
The probability plots show roughly the same thing. Most correct predictions are close to full confidence, while free-puck is more spread out. It is now a useful class, but some examples are still much less obvious to the model than others.
I also had a couple of dead ends. A hierarchical model, which first separated strikes from non-strikes and then classified the subtype, sounded sensible but treated most validation free-pucks as regular strikes. I tried another version with positive windows aligned directly around impact, and all five of its canonical validation free-pucks became regular strikes as well. In both cases I went back to the simpler flat five-class model.
There is still an important limit to the free-puck label. It means the wrist motion looks like the lift-and-strike examples in my training data. The Watch does not know whether it happened during a match or what the playing context was.
Finding the Beginning of the Swing
Once the CNN finds the rough strike range, I switch to the 800 Hz stream to refine contact. I then look backwards from contact to find the beginning of the swing.
For the self-toss strikes I usually practise, there is often a small movement when the ball leaves the hand, followed by the main swing. The analyser searches for the latest completed placement movement and uses its strongest acceleration peak as the start. If that is not clear, it can fall back to wrist rotation or the beginning of the main acceleration rise. Nearby aftershocks are grouped so that a smaller wobble does not move the start forward.
Time to Contact is simply the elapsed time from that selected start to contact. I use it as a timing measurement, not a quality score.
The raw-acceleration graph shows the two points used for this calculation: start and contact.
Ground strikes exposed another assumption in this calculation. They can have a detectable contact, but there is no self-toss placement movement to use as the start. For that reason, I do not calculate Time to Contact or projected Hand Speed for ground strikes. Wrist Rotation Speed and Peak Jerk can still be available around contact.
Turning Recordings Into Practice Sessions
Detection and metrics are not very useful if reviewing them is awkward. I wanted the normal flow to stay simple: start a session on the Watch, practise and rate the latest strike, stop the recording, then review the session on the iPhone.
The iPhone review shows detected attempts, my ratings, an optional note, summary measurements, charts, and estimated 3D paths. Ratings stay separate from the sensor data. They are my own assessment on a 0–10 scale. A zero can mark the latest candidate as a miss, while other ratings can sit beside Time to Contact, Projected Hand Speed, or Wrist Rotation Speed without changing those measurements.
The screenshot says 15 strikes were detected but refers to Strike 22 in one summary card. The number follows the complete attempt sequence, including misses and uncertain attempts that are hidden from the successful-strikes view.
After removing contact quality, I was careful not to rebuild the same idea with different-looking charts. The comparison view shows one measurement at a time, and all the bars use the same treatment. A taller bar is not marked as better. A shorter Time to Contact is not marked as an improvement. The bars are there to help me compare attempts from the same session.
Dominant, non-dominant, and dominant free-puck attempts are kept apart because their preparation and movement are different. Ground strikes are also left out of comparisons that depend on a self-toss start.
Comparing the Shape of a Swing
I wanted to keep working on the 3D view from Part 3, but comparing several reconstructed paths is difficult when they all begin in slightly different orientations.
The analyser now moves every path to a common origin, aligns its main pre-contact movement plane and early direction, and chooses a real strike near the centre of the group as the reference. The other paths are rotated towards it, and the same rotation is applied to their follow-through.
It does not mirror or resize the paths. It also does not stretch the timing to force different swings into the same shape. Differences should still be visible after alignment. The result is useful for comparison, but it is still an estimate of Watch movement rather than a measured path of the hand, hurley, or ball.
I can also save attempts as Reference Strikes. These are strikes I chose, not strikes the app judged to be best. A few saved strikes in one category can form a Reference Average for later sessions. Dominant, non-dominant, and dominant free-puck references are kept separate.
I like this direction much more than the old contact-quality score. Instead of trying to define a universally good strike, I can compare a new session with examples that felt useful to me.
Two Months of Personal Progress
The part I find most satisfying is not the model evaluation. It is finally having enough practice history to see whether my own measurements are changing.
The Home screen has progress cards for Time to Contact, Projected Hand Speed, and Wrist Rotation Speed. Each point is the average of valid strikes from one session. When I record more than one session on the same day, they share one point on the chart, but I can still open the individual sessions.
The large number is the latest measured session average. The dashed line is an estimated trend across the compatible sessions. I use a robust trend calculation so that one unusually high or low day has less influence, and the line only appears after there are enough sessions and strikes.
My current charts cover 26 sessions and 402 measured strikes across roughly two months. Over that period, Time to Contact is estimated to be 21% faster and Wrist Rotation Speed is estimated to be 10% faster.
There is a great feedback loop here. I needed more data for the app, so I kept going out to practise. Because I practised more, I started to improve, and the measurements began to move as well.
I can feel that my striking has improved, but the percentages are not a measurement of my overall hurling ability. They describe two signals from one sensor on my wrist. Accuracy, technique, decision-making, playing under pressure, and plenty of other parts of the game are outside these charts.
What the Watch Still Cannot Tell Me
The limitations from Part 3 have not disappeared.
Projected Hand Speed comes from integrating acceleration over a short swing window. It is useful when comparing similar recordings, but it is not hurley-head speed or ball speed.
The 3D path still contains drift, and a Watch on the wrist cannot account for the full movement of the hurley. Peak Jerk helps me find contact, but it changes with the ball, grip, vibration, and where contact happened. None of these measurements can tell the whole story on their own.
The project started as a strike detector, then briefly became an attempt to score strike quality. Now it feels more useful as a training log: record a session, recognise different attempts, compare their timing and movement, and look back over repeated practice.
The new Apple Watch Series 11 gives me better raw data, but it still cannot tell me whether a strike was good. That part is much more complicated than collecting more samples. For now, I am happy for the app to measure a few things honestly and give me another reason to keep practising.
What Is Next
The next step is user testing. I plan to share the TestFlight build with teammates and other people who are interested, then see how the app behaves with different players and practice routines.
I also want to explore having two recording modes. Training Mode would keep the current focus on individual strikes and practice-session review. Play Mode would look at the broader activity during a match or training game, starting with time spent running or idle and a few other session-level measurements. This is still an early idea, so I have not decided exactly which metrics will be useful yet.