Skip to content

Instantly share code, notes, and snippets.

@micolous
Created December 21, 2016 07:23
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save micolous/123ac1b7c3e9fb389e259ccd63df7992 to your computer and use it in GitHub Desktop.
Prototype reader for MyWay card dumps.
#!/usr/bin/env python
# -*- mode: python; indent-tabs-mode: nil; tab-width: 2 -*-
"""
myway_reader.py
Prototype reader for MyWay (ACT) card dumps.
See https://github.com/micolous/metrodroid/wiki/MyWay for format details.
Copyright 2016 Michael Farrell <micolous+git@gmail.com>
Note: This loads the entire file into memory blindly. Do not use on large file
sizes, as the memory usage of this file is extremely inefficient.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import print_function
from argparse import ArgumentParser, FileType
from struct import unpack
from datetime import datetime, timedelta
EPOCH = datetime(2000,1,1,0,0)
def parse_myway(input_data):
if len(input_data) != 1024:
raise Exception, 'mfc data must be exactly 1024 bytes'
# don't know how to check header yet
# Read balance (sector 2&3, block 2)
for off in (0xa0, 0xe0):
# TODO: figure out balance priorities
balance = unpack('<7xH7x', input_data[off:off+16])[0]
balance /= 100.
print('Balance: $%.2f' % balance)
# Read trips (sector 10-12, blocks 0-2)
trips = []
for off in (0x280, 0x290, 0x2a0, 0x2c0, 0x2d0, 0x2e0, 0x300, 0x310, 0x320):
ts, tap_on, route, cost = unpack('<3xLB4s1xH1x', input_data[off:off+16])
ts = EPOCH + timedelta(seconds=ts)
tag_on = (tap_on & 0x10 == 0x10)
route = route.rstrip('\0')
cost /= 100.
trips.append((ts.isoformat(), cost, route, 'on' if tag_on else 'off'))
# Place trips in chronological order to make things nicer.
trips.sort(key=lambda x: x[0])
for trip in trips:
print('%s: $%.2f %s Tag %s' % trip)
def main():
parser = ArgumentParser()
parser.add_argument('input', type=FileType('rb'), nargs=1)
options = parser.parse_args()
parse_myway(options.input[0].read())
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment