<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>vincentsparkes &#187; Bike Hack</title>
	<atom:link href="https://vince.sparkes.co/blog/?cat=18&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>https://vince.sparkes.co/blog</link>
	<description></description>
	<lastBuildDate>Sun, 08 Apr 2018 20:21:01 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.1.1</generator>
	<item>
		<title>Hacking the Everlast EV-410 Exercise Bike</title>
		<link>https://vince.sparkes.co/blog/?p=1834</link>
		<comments>https://vince.sparkes.co/blog/?p=1834#comments</comments>
		<pubDate>Wed, 10 Apr 2013 20:31:40 +0000</pubDate>
		<dc:creator><![CDATA[Vincent]]></dc:creator>
				<category><![CDATA[Bike Hack]]></category>

		<guid isPermaLink="false">http://vincentsparkes.co.uk/blog/?p=1834</guid>
		<description><![CDATA[I recently agreed to take part in the JDRF Ride to Cure Diabetes charity event. In preparation for the event I purchased an exercise bike, the Everlast EV-410. Relatively cheap and collapsible, it met every expectation, but could still be improved&#8230; It comes with a &#8220;computer&#8221; which displays distance, speed, calories burnt, time, and pulse. This &#8230; <a href="https://vince.sparkes.co/blog/?p=1834" class="more-link">Continue reading <span class="screen-reader-text">Hacking the Everlast EV-410 Exercise Bike</span></a>]]></description>
				<content:encoded><![CDATA[<p>I recently agreed to take part in the <a href="https://www.jdrf.org.uk/get-involved/join-in-an-event/ride" target="_blank">JDRF Ride to Cure Diabetes</a> charity event. In preparation for the event I purchased an exercise bike, the Everlast EV-410. Relatively cheap and collapsible, it met every expectation, but could still be improved&#8230;</p>
<p><a href="http://vincentsparkes.co.uk/blog/wp-content/uploads/2013/04/ev410.jpg"><img class="aligncenter size-full wp-image-1838" title="Everlast EV-410" src="http://vincentsparkes.co.uk/blog/wp-content/uploads/2013/04/ev410.jpg" alt="" width="300" height="300" /></a></p>
<p>It comes with a &#8220;computer&#8221; which displays distance, speed, calories burnt, time, and pulse. This is all well and good whilst using the bike, but what about recording progress over a period of time?</p>
<p>The simple solution would be to manually record this information. However I&#8217;d prefer to automate it, so set myself this little challenge. My plan is to connect it to the PC, ideally using WiFi and record the information automatically.</p>
<p>Thankfully the &#8220;computer&#8221; on the bike isn&#8217;t hardwired. The wheel of the bike has a cable that plugs into the computer via a standard 3.5mm audio jack. A little bit of research suggested that the cable is used to transmit a pulse for every revolution of the wheel.</p>
<p>To test this theory I invested in two cables, a splitter and a standard 3.5mm auxiliary cable.</p>
<p style="text-align: center;"><a href="http://vincentsparkes.co.uk/blog/wp-content/uploads/2013/04/WP_000112.jpg"><img class="aligncenter size-large wp-image-1839" title="Cable arrangement" src="http://vincentsparkes.co.uk/blog/wp-content/uploads/2013/04/WP_000112-768x1024.jpg" alt="" width="384" height="512" /></a></p>
<p>I used the splitter to intercept the signal (as shown above), and the auxiliary cable to connect the the bike to my PC via the microphone port. I then used the free software (<a href="http://audacity.sourceforge.net/" target="_blank">Audacity</a>) to record the input when riding the bike. As expected it produced a blip for every revolution!</p>
<p><a href="http://vincentsparkes.co.uk/blog/wp-content/uploads/2013/04/audaicty.png"><img class="aligncenter size-medium wp-image-1840" title="Audacity" src="http://vincentsparkes.co.uk/blog/wp-content/uploads/2013/04/audaicty-300x164.png" alt="" width="300" height="164" /></a></p>
<p>Next I needed a way to use this information. To begin with I wrote a short bit of java that reads from a WAV file, interprets the amplitude (<a href="http://ganeshtiwaridotcomdotnp.blogspot.co.uk/2011/12/java-extract-amplitude-array-from.html" target="_blank">this page came in exceptionally useful</a>), and counts the blips.</p>
<p>The next step was to get it to read directly from the bike. This involved learning a fair bit about how java handles sound, and to be honest, how computers in general handle sound!</p>
<p>Surprisingly there was very little existing code that demonstrates how to process sound input, so I had to write a majority of it from scratch. After some time experimenting I settled with the following solution.</p>
<ol>
<li>Find the audio mixers on the PC</li>
<li>Select the desired mixer</li>
<li>Open a line from the mixer</li>
<li>Calculate frame size (in my case 4bytes per frame, 44100 frames per second)</li>
<li>Use a stream to read from the line, frame by frame</li>
<li>Split each frame into 2 channels</li>
<li>Calculate amplitude of the left channel (right channel will be used to record pulse)</li>
</ol>
<p>This worked fairly well, and seemed to record the blips as expected. The next step was to integrate this with some form of timekeeping. Unfortunately it took around 3 seconds to process 1 second of audio. I tried changing buffer sizes, sample rates, and even tried skipping frames but to no avail. I settled on using a timer to keep track of the time, instead of the frame count.</p>
<p>Even with the lag, I&#8217;d come this far, so moved on to calculating the distance travelled for every revolution of the wheel. This was done by eye through experimentation. I settled at roughly 4.44m per revolution of the wheel, which yields distance covered the same as the bike computer.</p>
<p>Next up will be trying to find a way to overcome the lag, and designing a suitable way to store and display the information. The code I wrote is below for reference.</p>
<div id="_mcePaste">
<pre class="crayon-plain-tag">// Print system mixers
public void getMixers() {
    Mixer.Info[] mixers = AudioSystem.getMixerInfo();
    for(int i = 0; i &lt; mixers.length; i++) {
        Mixer.Info info = mixers[i];
        System.out.println(i + ": " + info.getName() + " - " + info.getDescription() + " - " + info.getVendor() + " - " + info.getVersion());
    }
}

// Is it a blip?
public static int level(int input) {
    if( input &gt;= 20000 )
        return 1;
    return 0;
}

public void getLine() {
    int mixerId = 6; // Manually chosen from running getMixers
    Mixer mixer = AudioSystem.getMixer(AudioSystem.getMixerInfo()[mixerId]);
    
    // Get lines from mixer
    Line.Info[] lines = mixer.getTargetLineInfo();
    for( int i = 0; i &lt; lines.length; i++) {
        DataLine.Info line = (DataLine.Info)lines[i];
        System.out.println(line);
    }

    try {
        // Open line from mixer
        TargetDataLine line = (TargetDataLine) mixer.getLine(lines[0]);
        line.open();

        // Fetch audio format of the line
        AudioFormat af = ((DataLine)line).getFormat();
        System.out.println(af.toString());

        // Create audio input stream from line
        AudioInputStream ais = new AudioInputStream(line);

        // Start listening on line
        line.start();
        int frameCount = 0;
        int secondCount = 0;
        int tickCount = 0;
        int previous = 0;
        double distance = 0;
        double distancePerTick = (double)10 / (double)2.25;
        ArrayList&lt;Integer&gt; ticksPerSec = new ArrayList&lt;Integer&gt;();
        long start = System.currentTimeMillis();
        int sampleSize = 4;

        // Process stream frame at a time
        while( true ) {
            byte[] frame = new byte[sampleSize];
            ais.read(frame, 0, sampleSize);

            if( af.getChannels() == 1 ) {
                // Single channel to be implemented later
            } else {
                // If two channels split frame in middle
                byte[] left = Arrays.copyOfRange(frame, 0, af.getFrameSize()/2);
                byte[] right = Arrays.copyOfRange(frame, af.getFrameSize()/2, af.getFrameSize());

                // Get amplitude
                int[] amp = WaveData.extractAmplitudeDataFromAmplitudeByteArray(af, left);

                if( level(amp[0]) == 1 &amp;&amp; previous == 0 ) {
                    tickCount++;
                }
                previous = level(amp[0]);
            }

            // Increment frame count
            frameCount++;

            if( System.currentTimeMillis() == start + 1000 ) {
                // A full sample rate has passed, equivalent to 1 second
                // Add tick count to arraylist
                ticksPerSec.add(tickCount);

                start = System.currentTimeMillis();
                distance = distance + (tickCount * distancePerTick);
                System.out.println(secondCount + ": " + distance + " - " + distancePerTick);

                // Move onto the next second
                secondCount++;

                // Reset frame and tick count
                frameCount = 0;
                tickCount = 0;
            }
        }
    } catch (LineUnavailableException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>https://vince.sparkes.co/blog/?feed=rss2&#038;p=1834</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
